| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @author Intel, Evgueni Brevnov, Ivan Volosyuk |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #define LOG_DOMAIN "vm.helpers" |
| #include "cxxlog.h" |
| |
| #include "lil.h" |
| #include "lil_code_generator.h" |
| #include "lil_code_generator_arch.h" |
| #include "lil_code_generator_utils.h" |
| #include "m2n.h" |
| #include "m2n_internal.h" |
| #include "tl/memory_pool.h" |
| #include "vm_ipf.h" |
| #include "vm_threads.h" |
| #include "stub_code_utils.h" |
| #include "open/vm_util.h" |
| #include "environment.h" |
| #include "port_malloc.h" |
| |
| #ifndef NDEBUG |
| #include "dump.h" |
| #endif |
| /************************ |
| * Where everything is |
| * (up = higher addresses / reg numbers) |
| * Stacked registers: |
| * |
| * |----------------------------| |
| * | outputs | |
| * |----------------------------| |
| * | ar.pfs, b0, sp (if needed) | |
| * |----------------------------| |
| * | locals | |
| * |----------------------------| |
| * | m2n frame (if needed) | |
| * |----------------------------| |
| * | inputs (8 spaces) | |
| * r32 |----------------------------| |
| * |
| * Other registers: |
| * - standard places go into r17-r18 |
| * - return values go into r8 or f8 |
| * - fp arguments go into f8-f15 |
| * - fp locals go into scratch registers if the stub makes no ordinary calls, otherwise onto the memory stack |
| * - r3 is used for add-long-immediate |
| * - r26-31 are used as temporaries in arithmetic operations |
| * |
| * Memory stack frame: |
| * |
| * |------------------------| |
| * | extra inputs | |
| * prev sp+16 |========================| |
| * | Local FR area | |
| * |------------------------| |
| * | FR input save area | |
| * |------------------------| |
| * | dyn alloc | |
| * |------------------------| |
| * | extra saves area | |
| * |------------------------| |
| * | extra outputs | |
| * sp+16 |------------------------| |
| * | scratch | |
| * sp |========================| |
| * |
| * All pieces of the stack are 16-byte aligned! |
| * |
| * If the stub executes calls other than tailcall and call.noret, |
| * inputs residing in FRs are moved to memory, to avoid being overwritten |
| * by FR outputs and return values and/or being used as scratch in the |
| * callee. In addition local floating point variables are put on the stack |
| * rather than in scratch registers. |
| * |
| * Things that are saved right after entry / restored right before ret: |
| * - ar.pfs (by the alloc instruction), in the stacked regs |
| * - b0 (only if the stub executes calls), in the stacked regs |
| * |
| * Things that are saved right before a call and restored right after a call |
| * (except for call.noret): |
| * - sp |
| * - gp, if needed |
| */ |
| |
| |
| // maximum number of locals. Increase it if needed, but not more than 80 or so. |
| #define MAX_LOCALS 10 |
| |
| // maximum number of stand places. Probably upper limit here is around 16. |
| #define MAX_STD_PLACES 2 |
| |
| // rounds up an integer value to the closest multiple of 16 |
| static inline unsigned align_16(unsigned n) { |
| return n = (n + 0xF) & ~0xF; |
| } |
| |
| |
| |
| // an enum indicating a variable's location: in a register class or on the |
| // stack (no LIL variable is ever on the heap!) |
| enum LcgIpfLocKind { |
| LLK_Gr, LLK_Fr, LLK_Stk, LLK_Stk16 |
| }; |
| |
| |
| // location of a LIL variable |
| struct LcgIpfLoc { |
| void *operator new(size_t sz, tl::MemoryPool& m) { |
| return m.alloc(sz); |
| } |
| void operator delete (void *p, tl::MemoryPool& m) { } |
| |
| LcgIpfLocKind kind; |
| int addr; // register number or SP-relative offset |
| |
| LcgIpfLoc(LcgIpfLocKind k, int a): kind(k), addr(a) |
| {} |
| |
| bool operator==(const LcgIpfLoc& loc) { |
| return (kind == loc.kind && addr == loc.addr); |
| } |
| |
| bool operator!=(const LcgIpfLoc& loc) { |
| return (kind != loc.kind || addr != loc.addr); |
| } |
| |
| private: |
| LcgIpfLoc(LcgIpfLoc &); // disable copying |
| LcgIpfLoc &operator=(LcgIpfLoc &); // disable copying |
| }; // struct LcgIpfLoc |
| |
| |
| // This class holds all relevant information needed in compilation, |
| // such as the mapping of variables to locations and the mapping of |
| // labels to ints. Some ofthis information is gathered during a prepass. |
| class LcgIpfContext: public LilInstructionVisitor { |
| private: |
| |
| LilCodeStub *cs; // the code stub |
| LilSig *entry_sig; // the stub's entry signature |
| bool is_arbitrary; // true if the stub's entry signature is "arbitrary" |
| tl::MemoryPool &mem; // a memory manager |
| |
| unsigned n_labels; // number of labels |
| LilLabel *labels; // a label's index is its label id |
| |
| // number of targets used by support functions, such as pop_m2n |
| unsigned extra_targets; |
| |
| |
| unsigned n_inputs; // total number of inputs |
| unsigned n_gr_inputs; // total number of GRs reserved for inputs |
| unsigned n_fr_inputs; // number of inputs placed in FRs |
| |
| unsigned first_local; // location of the first local variable |
| unsigned n_locals; // maximum number of locals |
| unsigned n_fr_locals; // maximum number of locals that may contain FP values |
| bool uses_local_fr[MAX_LOCALS]; // true if preserved fp f(16+index) is used by a local |
| unsigned local_fr_offset[MAX_LOCALS]; // offsets for local variable from start of local fr area |
| |
| unsigned first_output; // location of the start of the output space |
| unsigned n_outputs; // maximum number of outputs |
| unsigned n_gr_outputs; // total number of GRs reserved for outputs |
| unsigned n_fr_outputs; // maximum number of outputs placed in FRs |
| |
| unsigned first_gr_save; // start of region where ar.pfs, b0, etc. are saved |
| unsigned n_gr_saves; // number of GRs reserved for saving stuff |
| |
| unsigned stk_output_size; // bytes needed for outgoing params on the stack |
| unsigned stk_input_save_size; // size reserved for saving FR inputs |
| unsigned stk_local_fr_size; // size reserved for local FRs |
| unsigned stk_extra_saves_size; // bytes for m2n_save_all |
| |
| unsigned n_std_places; // number of standard places |
| unsigned stk_alloc_size; // size of allocatable memory on the stack |
| |
| // total size of the memory stack frame (in bytes) |
| unsigned stk_size; |
| |
| bool does_normal_calls; // true if the stub contains "normal" calls |
| bool does_tail_calls; // true if the stub contains tail calls |
| bool calls_unmanaged_code; // true if the stub calls calls code with a calling convention other than managed |
| bool has_m2n; // true if the stub contains push_m2n/pop_m2n instructions |
| bool uses_inputs; // true if inputs are ever accessed |
| bool needs_alloc; // true if an alloc instruction is needed at the start |
| |
| // an iterator used during pre-pass; it has to be stored here because |
| // it must be accessible from the visitor functions |
| LilInstructionIterator iter; |
| |
| public: |
| |
| void *operator new(size_t sz, tl::MemoryPool& m) { |
| return m.alloc(sz); |
| } |
| void operator delete (void *p, tl::MemoryPool& m) { } |
| |
| LcgIpfContext(LilCodeStub *cs, tl::MemoryPool &m): |
| cs(cs), |
| mem(m), |
| iter(cs, true) |
| { |
| // initialize some members |
| entry_sig = lil_cs_get_sig(cs); |
| is_arbitrary = lil_sig_is_arbitrary(entry_sig); |
| n_labels = 0; |
| extra_targets = 0; |
| labels = (LilLabel *) mem.alloc(lil_cs_get_num_instructions(cs) * sizeof (LilLabel)); |
| |
| n_locals = 0; |
| |
| for (unsigned i=0; i < MAX_LOCALS; i++) { |
| uses_local_fr[i] = false; |
| } |
| |
| n_outputs = 0; |
| n_fr_outputs = 0; |
| |
| n_std_places = 0; |
| |
| stk_extra_saves_size = 0; |
| stk_alloc_size = 0; |
| does_normal_calls = false; |
| calls_unmanaged_code = false; |
| does_tail_calls = false; |
| has_m2n = false; |
| uses_inputs = false; |
| |
| // get some info from the entry signature |
| if (is_arbitrary) { |
| n_inputs = 8; |
| n_gr_inputs = 8; |
| n_fr_inputs = 8; |
| } |
| else { |
| n_inputs = lil_sig_get_num_args(entry_sig); |
| n_gr_inputs = (n_inputs > 8) ? 8 : n_inputs; |
| n_fr_inputs = 0; |
| for (unsigned i=0; i<n_inputs; i++) { |
| LilType t = lil_sig_get_arg_type(entry_sig, i); |
| if (t == LT_F4 || t == LT_F8) |
| n_fr_inputs++; |
| } |
| } |
| |
| // scan the code stub for more information |
| while (!iter.at_end()) { |
| check_locals(iter.get_context()); |
| lil_visit_instruction(iter.get_current(), this); |
| iter.goto_next(); |
| } |
| |
| // decide if an alloc instruction is needed |
| needs_alloc = (has_m2n || does_normal_calls || n_outputs > 0 || n_locals > 0); |
| |
| // determine how many save spots are needed in stacked GRs |
| // ( if an M2N frame is present, registers will be saved in there) |
| if (has_m2n || !needs_alloc) |
| n_gr_saves = 0; |
| else if (does_normal_calls) |
| n_gr_saves = 4; // save ar.pfs, b0, gp, sp |
| else |
| n_gr_saves = 1; // save ar.pfs only |
| |
| |
| // decide where stacked register regions should start |
| if (has_m2n) |
| first_local = m2n_get_last_m2n_reg() + 1; |
| else |
| first_local = 32 + n_gr_inputs; |
| |
| first_gr_save = first_local + n_locals; |
| first_output = first_gr_save + n_gr_saves; |
| |
| // stack size needed for outputs (16-byte aligned) |
| if (n_outputs <= 8) { |
| n_gr_outputs = n_outputs; |
| stk_output_size = 0; |
| } |
| else { |
| n_gr_outputs = 8; |
| stk_output_size = align_16((n_outputs-8)*8); |
| } |
| |
| // 16-byte align allocatable space |
| stk_alloc_size = align_16(stk_alloc_size); |
| |
| // stack size needed for saving FR inputs |
| if (does_normal_calls) |
| stk_input_save_size = n_fr_inputs * 16; |
| else |
| stk_input_save_size = 0; |
| |
| // stack size needed for local FRs |
| stk_local_fr_size = 0; |
| if (does_normal_calls) { |
| for(unsigned i=0; i<MAX_LOCALS; i++) { |
| local_fr_offset[i] = stk_local_fr_size; |
| stk_local_fr_size += 16; |
| } |
| } |
| |
| // determine the size of the stack frame |
| stk_size = |
| stk_local_fr_size + // local FR space |
| stk_input_save_size + // FR input spill space |
| stk_alloc_size + // memory for dynamic allocation |
| stk_extra_saves_size + // memory for m2n_save_all |
| stk_output_size; // outputs on the stack |
| } // LcgIpfContext (constructor) |
| |
| // returns the code stub's entry signature |
| LilSig *get_entry_sig() { |
| return entry_sig; |
| } // get_entry_sig |
| |
| // true if the entry sig is arbitrary |
| bool entry_sig_is_arbitrary() { |
| return is_arbitrary; |
| } // entry_sig_is_arbitrary |
| |
| // returns the location of the first input GR |
| unsigned get_first_input_gr() { |
| return 32; |
| } // get_first_input_gr |
| |
| // returns the location of the first output GR |
| unsigned get_first_output_gr() { |
| return first_output; |
| } // get_first_output_gr |
| |
| // returns the number of targets used up by this stub, including |
| // targets used by labels and targets used by auxiliary functions, such as pop_m2n |
| unsigned get_num_targets() { |
| return n_labels + extra_targets; |
| } // get_num_labels |
| |
| // returns the id of a label |
| unsigned get_label_id(LilLabel label) { |
| for (unsigned i=0; i<n_labels; i++) { |
| if (labels[i] && strcmp(label, labels[i])==0) |
| return i; |
| } |
| |
| DIE(("Label not found")); |
| return 0; |
| } // get_label_id |
| |
| // returns an unused target id |
| // (for use by auxiliary functions, not intended for LIL labels!) |
| unsigned get_unused_target() { |
| assert(extra_targets > 0); // haven't run out of targets yet! |
| --extra_targets; |
| return n_labels + extra_targets; |
| } |
| |
| // returns the number of incoming arguments |
| unsigned get_num_inputs() { |
| return n_inputs; |
| } // get_num_input |
| |
| // returns the number of incoming arguments stored in FRs |
| unsigned get_num_fr_inputs() { |
| return n_fr_inputs; |
| } // get_num_fr_inputs |
| |
| // returns the size of the input space in the stacked GRs |
| unsigned get_input_space_size() { |
| return n_gr_inputs; |
| } // get_input_space_size |
| |
| // returns the size of the local space in the stacked GRs |
| // (this includes local vars, the M2N frame, and the save regs) |
| unsigned get_local_space_size() { |
| return first_output - (32 + n_gr_inputs); |
| } // get_local_space_size |
| |
| // returns the size of the output space in the stacked GRs |
| unsigned get_output_space_size() { |
| return n_gr_outputs; |
| } // get_output_space_size |
| |
| // returns true if there are floating-point inputs in registers f8-f15 that must be moved to registers f16-f23 |
| bool must_move_fr_inputs() { |
| return (does_normal_calls && n_fr_inputs > 0); |
| } // must_move_fr_inputs |
| |
| // returns true if ar.ccv should be set to zero before a call to managed |
| // code. This is the case if the stub or any function it calls is |
| // non-managed (i.e. it has a platform, jni, or stdcall cc) |
| bool must_reset_ccv() { |
| LilCc stub_cc = lil_sig_get_cc(entry_sig); |
| return !(stub_cc==LCC_Managed || stub_cc==LCC_Rth) || calls_unmanaged_code; |
| } // must_reset_ccv |
| |
| // returns the location of the i'th input |
| const LcgIpfLoc* get_input(unsigned n) { |
| // function should not be called if signature is arbitrary |
| assert(!is_arbitrary); |
| |
| LilType tp = lil_sig_get_arg_type(entry_sig, n); |
| |
| // if n >= 8, input is on stack |
| if (n >= 8) { |
| return new(mem) LcgIpfLoc(LLK_Stk, 16 + stk_size + (n-8)*8); |
| } |
| |
| if (tp != LT_F4 && tp != LT_F8) { |
| return new(mem) LcgIpfLoc(LLK_Gr, 32+n); |
| } |
| |
| // parameter is fp; its location depends on how many fp parameters |
| // come before it. |
| unsigned fp_param_cnt = 0; |
| for (unsigned i=0; i<n; i++) { |
| LilType t = lil_sig_get_arg_type(entry_sig, i); |
| if (t == LT_F4 || t == LT_F8) |
| fp_param_cnt++; |
| } |
| |
| if (must_move_fr_inputs()) { |
| // inputs have been moved to memory |
| return new(mem) LcgIpfLoc(LLK_Stk16, get_input_save_offset() + 16 * fp_param_cnt); |
| } |
| else { |
| return new(mem) LcgIpfLoc(LLK_Fr, 8+fp_param_cnt); |
| } |
| } // get_input |
| |
| |
| // returns the location of the i'th output |
| const LcgIpfLoc* get_output(unsigned n, LilSig *out_sig) { |
| LilType tp = lil_sig_get_arg_type(out_sig, n); |
| |
| // if n >= 8, output is in stack |
| if (n >= 8) { |
| return new(mem) LcgIpfLoc(LLK_Stk, 16 + (n-8)*8); |
| } |
| |
| if (tp != LT_F4 && tp != LT_F8) { |
| return new(mem) LcgIpfLoc(LLK_Gr, first_output + n); |
| } |
| |
| // parameter is fp; its location depends on how many fp parameters |
| // come before it. |
| unsigned fp_param_cnt = 0; |
| for (unsigned i=0; i<n; i++) { |
| LilType t = lil_sig_get_arg_type(out_sig, i); |
| if (t == LT_F4 || t == LT_F8) |
| fp_param_cnt++; |
| } |
| return new(mem) LcgIpfLoc(LLK_Fr, 8+fp_param_cnt); |
| } // get_output |
| |
| // returns the location of the i'th int local |
| const LcgIpfLoc* get_gr_local(unsigned n) { |
| return new(mem) LcgIpfLoc(LLK_Gr, first_local+n); |
| } // get_gr_local |
| |
| // returns the location of the i'th fp local |
| const LcgIpfLoc* get_fr_local(unsigned n) |
| { |
| if (does_normal_calls) |
| return new(mem) LcgIpfLoc(LLK_Stk16, get_local_fr_offset()+local_fr_offset[n]); |
| else |
| return new(mem) LcgIpfLoc(LLK_Fr, (n<8 ? 9+n : 32+n)); |
| } // get_fr_local |
| |
| // returns the GR number of the i'th standard place |
| unsigned get_std_place(unsigned n) { |
| return 17+n; |
| } // get_std_place |
| |
| // returns the location of a LIL variable |
| // (input, output, local, std place, or return value) |
| // inst: the current instruction |
| // ic: the current instruction context |
| // is_lval: true if the variable is used as an l-value |
| const LcgIpfLoc* get_var_loc(LilVariable *var, |
| LilInstruction *inst, |
| LilInstructionContext *ic, |
| bool is_lval) { |
| switch (lil_variable_get_kind(var)) { |
| case LVK_In: |
| return get_input(lil_variable_get_index(var)); |
| case LVK_StdPlace: |
| return new(mem) LcgIpfLoc(LLK_Gr, get_std_place(lil_variable_get_index(var))); |
| case LVK_Out: |
| { |
| LilSig *out_sig = lil_ic_get_out_sig(ic); |
| assert(out_sig != NULL); |
| return get_output(lil_variable_get_index(var), out_sig); |
| } |
| case LVK_Local: |
| { |
| unsigned index = lil_variable_get_index(var); |
| LilType t; |
| if (is_lval) |
| t = lil_instruction_get_dest_type(cs, inst, ic); |
| else |
| t = lil_ic_get_local_type(ic, index); |
| if (t == LT_F4 || t == LT_F8) |
| return get_fr_local(lil_variable_get_index(var)); |
| else |
| return get_gr_local(lil_variable_get_index(var)); |
| } |
| case LVK_Ret: |
| { |
| LilType t; |
| if (is_lval) |
| t = lil_instruction_get_dest_type(cs, inst, ic); |
| else |
| t = lil_ic_get_ret_type(ic); |
| if (t == LT_F4 || t == LT_F8) |
| return new(mem) LcgIpfLoc(LLK_Fr, 8); |
| return new(mem) LcgIpfLoc(LLK_Gr, 8); |
| } |
| |
| default: |
| DIE(("Unklown variable kind")); |
| return new(mem) LcgIpfLoc(LLK_Gr, 0); // should never be reached |
| } |
| } // get_var_loc |
| |
| |
| // returns the GR number where the result of the alloc instruction |
| // (old ar.pfs) should be saved |
| const LcgIpfLoc* get_pfs_save_gr() { |
| if (!needs_alloc) |
| return NULL; // doesn't need saving |
| if (has_m2n) |
| return new(mem) LcgIpfLoc(LLK_Stk, m2n_get_pfs_save_reg()); |
| return new(mem) LcgIpfLoc(LLK_Stk, first_gr_save); |
| } // get_pfs_save_gr |
| |
| // returns the GR number where b0 should be saved |
| // (0 if b0 does not need saving) |
| const LcgIpfLoc* get_return_save_gr() { |
| if (!has_m2n && !does_normal_calls) |
| return NULL; // b0 does not need saving |
| |
| if (has_m2n) |
| return new(mem) LcgIpfLoc(LLK_Gr, m2n_get_return_save_reg()); |
| else |
| return new(mem) LcgIpfLoc(LLK_Gr, first_gr_save + 1); |
| } // get_return_save_gr |
| |
| // returns the GR number where gp should be saved |
| const LcgIpfLoc* get_gp_save_gr() { |
| if (has_m2n) |
| return new(mem) LcgIpfLoc(LLK_Gr, m2n_get_gp_save_reg()); |
| else if (!does_normal_calls) |
| return NULL; // no need to save gp |
| return new(mem) LcgIpfLoc(LLK_Gr, first_gr_save + 2); |
| } // get_gp_save_gr |
| |
| // returns the size of the stack frame |
| unsigned get_stk_size() { |
| return stk_size; |
| } // get_stk_size |
| |
| // returns the sp-relative offset of the start of the input register save space |
| unsigned get_input_save_offset() { |
| return 16 + stk_output_size + stk_extra_saves_size + stk_alloc_size; |
| } // get_input_save_offset |
| |
| // returns the sp-relative offset of the start of the local FR space |
| unsigned get_local_fr_offset() { |
| return 16 + stk_output_size + stk_extra_saves_size + stk_alloc_size + stk_input_save_size; |
| } // get_input_save_offset |
| |
| // returns the sp-relative offset of the first "allocatable" byte |
| unsigned get_alloc_start_offset() { |
| return 16 + stk_output_size + stk_extra_saves_size; |
| } // get_alloc_start_offset |
| |
| unsigned get_extra_saves_start_offset() { |
| return 16 + stk_output_size; |
| } |
| |
| bool has_push_m2n() { |
| return has_m2n; |
| } |
| |
| private: |
| //***************** |
| // helper functions, used by visitor functions |
| |
| // gather info from variable |
| void check_variable(LilVariable *var) { |
| if (lil_variable_get_kind(var) == LVK_In) |
| uses_inputs = true; |
| } // check_variable |
| |
| // gather info from operand |
| void check_operand(LilOperand *o) { |
| if (o != NULL && !lil_operand_is_immed(o)) |
| check_variable(lil_operand_get_variable(o)); |
| } // check_operand |
| |
| // check a LIL context for local usage |
| void check_locals(LilInstructionContext *ic) { |
| unsigned nl = lil_ic_get_num_locals(ic); |
| assert(nl <= MAX_LOCALS); |
| if (n_locals < nl) |
| n_locals = nl; |
| for (unsigned i=0; i < nl; i++) { |
| LilType t = lil_ic_get_local_type(ic, i); |
| if (t == LT_F4 || t == LT_F8) { |
| uses_local_fr[i] = true; |
| } |
| } |
| } // check_locals |
| |
| |
| //************************** |
| // visitor functions |
| |
| void label(LilLabel label) { |
| labels[n_labels++] = label; |
| } // label |
| |
| void locals(unsigned l) { |
| // nothing to do; locals are taken care of in check_locals() |
| } // locals |
| |
| void std_places(unsigned sp) { |
| assert(sp<=MAX_STD_PLACES); |
| if (sp > n_std_places) |
| n_std_places = sp; |
| } // std_places |
| |
| void alloc(LilVariable* var, unsigned alloc_space) { |
| alloc_space = (alloc_space + 0x7) & ~0x7; |
| stk_alloc_size += alloc_space; |
| } // alloc |
| |
| void asgn(LilVariable* var, enum LilOperation operation, LilOperand* op1, LilOperand* op2) { |
| check_variable(var); |
| check_operand(op1); |
| if (lil_operation_is_binary(operation)) |
| check_operand(op2); |
| } // asgn |
| |
| void ts(LilVariable*var) { |
| check_variable(var); |
| } // ts |
| |
| void handles(LilOperand* op) { |
| check_operand(op); |
| } // handles |
| |
| void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) { |
| check_variable(dst); |
| if (base != NULL) |
| check_variable(base); |
| if (index != NULL) |
| check_variable(index); |
| } // ld |
| |
| void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilOperand* src) { |
| if (base != NULL) |
| check_variable(base); |
| if (index != NULL) |
| check_variable(index); |
| check_operand(src); |
| } // st |
| |
| void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel) { |
| if (base != NULL) |
| check_variable(base); |
| if (index != NULL) |
| check_variable(index); |
| } // inc |
| |
| void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, |
| LilOperand* cmp, LilOperand* src, LilLabel) |
| { |
| if (base) check_variable(base); |
| if (index) check_variable(index); |
| check_operand(cmp); |
| check_operand(src); |
| } |
| |
| void j(LilLabel) { |
| // nothing to do |
| } // j |
| |
| void jc(LilPredicate p, LilOperand* o1, LilOperand* o2, LilLabel) |
| { |
| check_operand(o1); |
| if (lil_predicate_is_binary(p)) |
| check_operand(o2); |
| } // jc |
| |
| void out(LilSig* sig) { |
| // make sure there is enough space for this command's outputs |
| unsigned outs = lil_sig_get_num_args(sig); |
| if (outs > n_outputs) |
| n_outputs = outs; |
| |
| // reserve enough FR outputs |
| unsigned n_fo = 0; |
| for (unsigned i=0; i<lil_sig_get_num_args(sig); i++) { |
| LilType t = lil_sig_get_arg_type(sig, i); |
| if (t == LT_F4 || t == LT_F8) |
| n_fo ++; |
| } |
| if (n_fo > n_fr_outputs) |
| n_fr_outputs = n_fo; |
| |
| // check if this refers to a call to unmanaged code |
| LilCc cc = lil_sig_get_cc(sig); |
| if (cc != LCC_Managed && cc != LCC_Rth) |
| calls_unmanaged_code = true; |
| } // out |
| |
| void in2out(LilSig* sig) { |
| // reserve enough outputs and FR outputs |
| if (n_inputs > n_outputs) |
| n_outputs = n_inputs; |
| if (n_fr_inputs > n_fr_outputs) |
| n_fr_outputs = n_fr_inputs; |
| // in2out implies that inputs are being accessed |
| uses_inputs = true; |
| |
| // check if this refers to a call to unmanaged code |
| LilCc cc =lil_sig_get_cc(sig); |
| if (cc != LCC_Managed && cc != LCC_Rth) |
| calls_unmanaged_code = true; |
| |
| } // in2out |
| |
| void call(LilOperand* o, LilCallKind k) { |
| check_operand(o); |
| if (k == LCK_Call) |
| does_normal_calls = true; |
| else if (k == LCK_TailCall) { |
| does_tail_calls = true; |
| // no need to reserve extra outputs, like in in2out, since tailcall is implemented differently |
| } |
| } // call |
| |
| void ret() { |
| // nothing to do |
| } // ret |
| |
| void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { |
| has_m2n = true; // remember that this stub requires an m2n frame |
| } // push_m2n |
| |
| void m2n_save_all() |
| { |
| stk_extra_saves_size = M2N_EXTRA_SAVES_SPACE; |
| } // m2n_save_all |
| |
| void pop_m2n() { |
| // if handles are present, pop_m2n may be using an extra target |
| bool handles = lil_ic_get_m2n_state(iter.get_context()) == LMS_Handles; |
| if (handles) { |
| // this pop_m2n will use an extra target |
| extra_targets++; |
| // it will also execute a call |
| does_normal_calls = true; |
| } |
| } |
| |
| void print(char *, LilOperand *) { |
| // this is a no-op if debugging is off |
| #ifdef STUB_DEBUG |
| // reserve at least 2 outputs; remember that a call will be made |
| if (n_outputs < 2) |
| n_outputs = 2; |
| does_normal_calls = true; |
| #endif // STUB_DEBUG |
| } // print |
| |
| //visitor functions - end |
| //*********************** |
| |
| }; //class LcgIpfContext |
| |
| |
| // the following class is a LIL instruction visitor used by |
| // LilCodeGeneratorIpf to do most of the work. |
| // After the constructor exits, all the necessary code is in the |
| // code emitter. |
| class LcgIpfCodeGen: public LilInstructionVisitor { |
| |
| // the following variables are here because visitor functions need to |
| // access them in addition to their arguments |
| LilCodeStub *cs; |
| Merced_Code_Emitter& emitter; |
| LcgIpfContext& context; |
| tl::MemoryPool& mem; |
| LilInstructionIterator iter; |
| LilInstructionContext *ic; |
| LilInstruction *inst; |
| // visit functions can always assume that inst points to the current instruction and ic points to the current context |
| |
| unsigned current_alloc; // keeps track of memory allocation |
| |
| LilSig *cur_out_sig; // keeps track of the current out signature |
| |
| // some useful constants |
| static const LcgIpfLoc* gp; |
| static const LcgIpfLoc* sp; |
| static const LcgIpfLoc* r0; |
| static const LcgIpfLoc* tp; // thread pointer |
| static const LcgIpfLoc* tmp_op1; // used for temp storage of operands |
| static const LcgIpfLoc* tmp_op2; // used for temp storage of operands |
| static const LcgIpfLoc* tmp_res; // used for temp storage of results |
| static const LcgIpfLoc* tmp_f; // used for temp storage of FPs |
| static const LcgIpfLoc* tmp_addr1; // used for temp storage of addresses |
| static const LcgIpfLoc* tmp_addr2; // used for temp storage of addresses |
| static const LcgIpfLoc* tmp_addr3; // used for temp storage of addresses |
| static const LcgIpfLoc* tmp_addl; // used as a temporary op of addl |
| static const unsigned tmp_pred; // used for the jc instruction |
| static const unsigned tmp_br; // used for indirect calls |
| |
| private: |
| /******************* |
| * helper functions |
| */ |
| |
| // performs an immediate addition; may use adds, addl, or movl and add, |
| // depending on the immediate operand's size |
| void add_imm(const LcgIpfLoc *dest, const LcgIpfLoc *src, int64 imm) { |
| assert(src->kind == LLK_Gr && (dest->kind==LLK_Stk || dest->kind==LLK_Gr)); |
| |
| unsigned dst_reg = (dest->kind==LLK_Stk ? tmp_res->addr : dest->addr); |
| |
| if (imm >= -0x2000 && imm < 0x2000) { |
| // imm fits into 14 bits; use adds |
| emitter.ipf_adds(dst_reg, (int) imm, src->addr, current_predicate); |
| } |
| else if (imm >= -0x200000 && imm < 0x200000) { |
| // imm fits into 22 bits; use addl |
| emitter.ipf_addl(dst_reg, (int) imm, src->addr, current_predicate); |
| } |
| else { |
| // have to use movl |
| unsigned lower_32 = (unsigned) imm; |
| unsigned upper_32 = (unsigned) (imm >> 32); |
| if (src->addr==0) { |
| emitter.ipf_movl(dst_reg, upper_32, lower_32, current_predicate); |
| } else { |
| emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); |
| emitter.ipf_add(dst_reg, src->addr, tmp_addl->addr, current_predicate); |
| } |
| } |
| |
| if (dest->kind==LLK_Stk) |
| move(dest, tmp_res); |
| } |
| |
| void int_mul(unsigned dest_reg, unsigned src1_reg, unsigned src2_reg) |
| { |
| unsigned ftmp1 = 7, ftmp2 = 8, ftmp3 = 9; |
| emitter.ipf_setf(freg_sig, ftmp1, src1_reg, current_predicate); |
| emitter.ipf_setf(freg_sig, ftmp2, src2_reg, current_predicate); |
| emitter.ipf_xma(ftmp3, ftmp1, ftmp2, 0, l_form, current_predicate); |
| emitter.ipf_getf(freg_sig, dest_reg, ftmp3); |
| } |
| |
| // binary arithmetic operations without immediates |
| // (allowed only for integer values!) |
| void bin_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src1, const LcgIpfLoc* src2) |
| { |
| assert((dest->kind == LLK_Gr || dest->kind == LLK_Stk) && |
| (src1->kind == LLK_Gr || src1->kind == LLK_Stk) && |
| (src2->kind == LLK_Gr || src2->kind == LLK_Stk)); |
| |
| unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; |
| unsigned src1_reg = (src1->kind == LLK_Stk) ? tmp_op1->addr : src1->addr; |
| unsigned src2_reg = (src2->kind == LLK_Stk) ? tmp_op2->addr : src2->addr; |
| |
| if (src1->kind == LLK_Stk) { |
| move(tmp_op1, src1); // load src1 into tmp_op1 |
| } |
| if (src2->kind == LLK_Stk) { |
| move(tmp_op2, src2); // load src2 into tmp_op2 |
| } |
| |
| switch (o) { |
| case LO_Add: |
| emitter.ipf_add(dest_reg, src1_reg, src2_reg, current_predicate); |
| break; |
| case LO_Sub: |
| emitter.ipf_sub(dest_reg, src1_reg, src2_reg, current_predicate); |
| break; |
| case LO_SgMul: |
| int_mul(dest_reg, src1_reg, src2_reg); |
| break; |
| case LO_Shl: |
| emitter.ipf_shl(dest_reg, src1_reg, src2_reg, current_predicate); |
| break; |
| default: |
| DIE(("Unexpected operation")); // control should never reach this point |
| } |
| |
| if (dest->kind == LLK_Stk) { |
| // store tmp_res back to dest |
| move(dest, tmp_res); |
| } |
| } // bin_op |
| |
| |
| // binary op where the second op is immediate |
| // (allowed only for integer values!) |
| void bin_op_imm(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src, POINTER_SIZE_INT imm_val) { |
| int64 imm = (int64) imm_val; // convert to signed |
| assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); |
| const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; |
| const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; |
| |
| if (src_reg != src) { |
| move(src_reg, src); // load src into tmp_op1 |
| } |
| |
| if (o == LO_Shl) { |
| if (imm > 63) { |
| emitter.ipf_add(dest_reg->addr, 0, 0, current_predicate); // dest_reg = 0 |
| } |
| else { |
| emitter.ipf_shli(dest_reg->addr, src_reg->addr, (int) imm, current_predicate); |
| } |
| } |
| else if (o == LO_Sub) { |
| add_imm(dest_reg, src_reg, -imm); |
| } |
| else if (o == LO_Add) { |
| add_imm(dest_reg, src_reg, imm); |
| } |
| else if (o == LO_SgMul) { |
| // This is not as optimised as it could be |
| // We could move the immediate directly into a floating point register |
| unsigned lower_32 = (unsigned) imm; |
| unsigned upper_32 = (unsigned) (imm >> 32); |
| emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); |
| int_mul(dest_reg->addr, tmp_addl->addr, src_reg->addr); |
| } |
| else if (o == LO_And) { |
| if (imm>=0x80 && imm<0x80) { |
| emitter.ipf_and(dest_reg->addr, src_reg->addr, (unsigned)imm, current_predicate); |
| } else { |
| unsigned lower_32 = (unsigned) imm; |
| unsigned upper_32 = (unsigned) (imm >> 32); |
| emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); |
| emitter.ipf_and(dest_reg->addr, tmp_addl->addr, src_reg->addr, current_predicate); |
| } |
| } else { |
| DIE(("Unexpected operation")); // invalid value in o |
| } |
| |
| if (dest_reg != dest) { |
| //move tmp_res back to dest |
| move(dest, dest_reg); |
| } |
| } // bin_op_imm |
| |
| // subtract op where the first op is immediate |
| // (allowed only for intefer values) |
| void sub_op_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val, const LcgIpfLoc* src) |
| { |
| int64 imm = (int64)imm_val; |
| assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); |
| const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; |
| const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; |
| |
| if (src_reg!=src) |
| move(src_reg, src); |
| |
| if (imm>=-0x80 && imm<0x80) { |
| emitter.ipf_subi(dest_reg->addr, (unsigned)imm, src_reg->addr, current_predicate); |
| } else { |
| move_imm(tmp_res, imm); |
| emitter.ipf_sub(dest_reg->addr, tmp_res->addr, src_reg->addr, current_predicate); |
| } |
| |
| if (dest_reg!=dest) |
| move(dest, dest_reg); |
| } |
| |
| // unary operation without immediates |
| void un_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src) { |
| assert(dest->kind != LLK_Fr && src->kind != LLK_Fr); |
| unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; |
| unsigned src_reg = (src->kind == LLK_Stk) ? tmp_op1->addr : src->addr; |
| |
| if (src->kind == LLK_Stk) { |
| move(tmp_op1, src); // load src into tmp_op1 |
| } |
| |
| switch (o) { |
| case LO_Neg: |
| emitter.ipf_sub(dest_reg, 0, src_reg, current_predicate); |
| break; |
| case LO_Not: |
| emitter.ipf_xori(dest_reg, 0xFF, src_reg, current_predicate); |
| break; |
| case LO_Sx1: |
| emitter.ipf_sxt(sxt_size_1, dest_reg, src_reg, current_predicate); |
| break; |
| case LO_Sx2: |
| emitter.ipf_sxt(sxt_size_2, dest_reg, src_reg, current_predicate); |
| break; |
| case LO_Sx4: |
| emitter.ipf_sxt(sxt_size_4, dest_reg, src_reg, current_predicate); |
| break; |
| case LO_Zx1: |
| emitter.ipf_zxt(sxt_size_1, dest_reg, src_reg, current_predicate); |
| break; |
| case LO_Zx2: |
| emitter.ipf_zxt(sxt_size_2, dest_reg, src_reg, current_predicate); |
| break; |
| case LO_Zx4: |
| emitter.ipf_zxt(sxt_size_4, dest_reg, src_reg, current_predicate); |
| break; |
| default: |
| DIE(("Unexpected operation")); // control should never reach this point |
| } |
| |
| if (dest->kind == LLK_Stk) { |
| //move tmp_res back to dest |
| move(dest, tmp_res); |
| } |
| } // un_op |
| |
| |
| // move between two register or stack locations |
| void move(const LcgIpfLoc* dest, const LcgIpfLoc* src) { |
| if (dest->kind == LLK_Stk) { |
| // put sp + (dest offset) in tmp_addr1 |
| add_imm(tmp_addr1, sp, dest->addr); |
| |
| if (src->kind == LLK_Stk) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load src into tmp_op1 |
| emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, tmp_op1->addr, tmp_addr2->addr, current_predicate); |
| // store tmp_op1 into dest |
| emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, tmp_op1->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Stk16) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load (128-bit) src into tmp_f |
| emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); |
| // store (64-bit) tmp_f into dest |
| emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Gr) { |
| // store src into dest |
| emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Fr) { |
| // store src into (64-bit) dest |
| emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, src->addr, current_predicate); |
| } |
| } |
| else if (dest->kind == LLK_Stk16) { |
| // put sp + (dest offset) in tmp_addr1 |
| add_imm(tmp_addr1, sp, dest->addr); |
| |
| if (src->kind == LLK_Stk) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load (64-bit) src into tmp_f |
| emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); |
| // store tmp_f into (128-bit) dest |
| emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Stk16) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load (128-bit) src into tmp_f |
| emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); |
| // store tmp_f into (128-bit) dest |
| emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Fr) { |
| // store src into (128-bit) dest |
| emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // src shouldn't be a GR! |
| } |
| } |
| else if (dest->kind == LLK_Gr) { |
| if (src->kind == LLK_Stk) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load src into dest |
| emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Gr) { |
| // move src into dest |
| emitter.ipf_mov(dest->addr, src->addr, current_predicate); |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // src->kind shouldn't be LLK_Fr or LLK_Stk16 |
| } |
| } |
| else if (dest->kind == LLK_Fr) { |
| if (src->kind == LLK_Stk) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load (64-bit) src into dest |
| emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, dest->addr, tmp_addr2->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Stk16) { |
| // put sp + (src offset) in tmp_addr2 |
| add_imm(tmp_addr2, sp, src->addr); |
| // load (128-bit) src into dest |
| emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); |
| } |
| else if (src->kind == LLK_Fr) { |
| // move src into dest |
| emitter.ipf_fmov(dest->addr, src->addr, current_predicate); |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // src should not be GR! |
| } |
| } |
| else { |
| DIE(("Unknown kind")); // dest->kind has illegal value! |
| } |
| } // move |
| |
| |
| // move immediate |
| // (not allowed for FRs!) |
| void move_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val) { |
| assert(dest->kind==LLK_Gr || dest->kind==LLK_Stk); |
| add_imm(dest, r0, (int64)imm_val); |
| } // move_imm |
| |
| // generate instructions that compute a condition into two predicate registers |
| void do_cond(LilPredicate p, LilOperand* op1, LilOperand* op2, unsigned true_pr, unsigned false_pr) |
| { |
| assert(current_predicate==0); |
| |
| // make sure operands are in registers |
| unsigned op1_reg = 0, op2_reg = 0; // GR numbers holding op1 and op2 |
| |
| if (lil_operand_is_immed(op1)) { |
| int64 imm = (int64) lil_operand_get_immed(op1); |
| move_imm(tmp_op1, imm); |
| op1_reg = tmp_op1->addr; |
| } |
| else { // op1 is a variable |
| LilVariable *op1_var = lil_operand_get_variable(op1); |
| const LcgIpfLoc* op1_loc = |
| context.get_var_loc(op1_var, inst, ic, false); |
| if (op1_loc->kind == LLK_Stk) { |
| // load op1_loc into tmp_op1 |
| move(tmp_op1, op1_loc); |
| op1_reg = tmp_op1->addr; |
| } |
| else if (op1_loc->kind == LLK_Gr) { |
| assert(op1_loc->kind != LLK_Fr); // comparisons available only for ints! |
| op1_reg = op1_loc->addr; |
| } |
| else { |
| DIE(("Wrong kind")); // comparisons available only for ints! |
| } |
| } |
| |
| if (lil_predicate_is_binary(p)) { |
| if (lil_operand_is_immed(op2)) { |
| int64 imm = (int64) lil_operand_get_immed(op2); |
| move_imm(tmp_op2, imm); |
| op2_reg = tmp_op2->addr; |
| } |
| else { // op2 is a variable |
| LilVariable *op2_var = lil_operand_get_variable(op2); |
| const LcgIpfLoc* op2_loc = |
| context.get_var_loc(op2_var, inst, ic, false); |
| if (op2_loc->kind == LLK_Stk) { |
| // load op2_loc into tmp_op2 |
| move(tmp_op2, op2_loc); |
| op2_reg = tmp_op2->addr; |
| } |
| else if (op2_loc->kind == LLK_Gr) { |
| op2_reg = op2_loc->addr; |
| } |
| else { |
| DIE(("Wrong kind")); // comparisons available only for ints! |
| } |
| } |
| } |
| else { // the predicate is unary, i.e. the 2nd op is r0 |
| op2_reg = 0; |
| } |
| |
| // do the comparison using a cmp instruction, place result in tmp_pred |
| Int_Comp_Rel emitter_p = icmp_invalid; |
| switch (p) { |
| case LP_IsZero: |
| case LP_Eq: |
| emitter_p = icmp_eq; break; |
| case LP_IsNonzero: |
| case LP_Ne: |
| emitter_p = icmp_ne; break; |
| case LP_Le: |
| emitter_p = icmp_le; break; |
| case LP_Lt: |
| emitter_p = icmp_lt; break; |
| case LP_Ule: |
| emitter_p = icmp_leu; break; |
| case LP_Ult: |
| emitter_p = icmp_ltu; break; |
| default: |
| DIE(("Unknown predicate")); // should never be reached |
| } |
| |
| emitter.ipf_cmp(emitter_p, cmp_unc, true_pr, false_pr, op1_reg, op2_reg, false, 0); |
| } |
| |
| // generate instructions that calculate a LIL address; |
| // return the GR number where the address can be found |
| // (this can be either the base register itself, or a temp register) |
| const LcgIpfLoc* do_addr(LilVariable *base, unsigned scale, LilVariable *index, POINTER_SIZE_INT offset) { |
| // locations for base and index (NULL if base or index doesn't exist) |
| const LcgIpfLoc *bloc=NULL, *iloc=NULL; |
| // shift amount, if index exists |
| unsigned shift=0; |
| |
| if (base != NULL) { |
| const LcgIpfLoc* orig_bloc = |
| context.get_var_loc(base, inst, ic, false); |
| assert(orig_bloc->kind == LLK_Gr || orig_bloc->kind == LLK_Stk); |
| if (orig_bloc->kind == LLK_Stk) { |
| move(tmp_addr1, orig_bloc); |
| bloc = tmp_addr1; |
| } |
| else { |
| bloc = orig_bloc; |
| } |
| } |
| |
| if (index != NULL && scale != 0) { |
| const LcgIpfLoc* orig_iloc = |
| context.get_var_loc(index, inst, ic, false); |
| assert(orig_iloc->kind == LLK_Gr || orig_iloc->kind == LLK_Stk); |
| |
| // fetch index from memory if needed |
| if (orig_iloc->kind == LLK_Stk) { |
| move(tmp_res, orig_iloc); |
| iloc = tmp_addr2; |
| } |
| else { |
| iloc = orig_iloc; |
| } |
| |
| // determine size of shift |
| while (scale > 1) { |
| scale >>= 1; |
| shift++; |
| } |
| assert(shift >= 1 && shift <= 4); |
| } |
| |
| if (bloc) { |
| if (offset) { |
| add_imm(tmp_addr3, bloc, offset); |
| if (iloc) |
| emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); |
| return tmp_addr3; |
| } |
| else { // no offset |
| if (iloc) { |
| emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, bloc->addr, current_predicate); |
| return tmp_addr3; |
| } |
| else // no index |
| return bloc; |
| } |
| } // if (bloc) |
| else { // no base |
| if (offset) { |
| move_imm(tmp_addr3, offset); |
| if (iloc) |
| emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); |
| } |
| else { // no offset |
| if (iloc) |
| emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, 0, current_predicate); |
| else // no index |
| DIE(("Can't have no base, no index and no offset")); |
| } |
| return tmp_addr3; |
| } // else (bloc) |
| } // do_addr |
| |
| |
| // implements the copying of incoming to outgoing args in in2out and tailcall |
| void do_in_to_out() |
| { |
| assert(!context.entry_sig_is_arbitrary()); |
| unsigned n_inputs = context.get_num_inputs(); |
| unsigned i; |
| for (i=0; i<8 && i<n_inputs; i++) { |
| const LcgIpfLoc* in_loc = context.get_input(i); |
| const LcgIpfLoc* out_loc = context.get_output(i, cur_out_sig); |
| if (in_loc != out_loc) { |
| move(out_loc, in_loc); |
| } |
| } |
| if (i==n_inputs) return; |
| |
| // The rest of the inputs and outputs are on the stack, so use an optimised memory copy |
| const LcgIpfLoc* in_loc = context.get_input(8); |
| const LcgIpfLoc* out_loc = context.get_output(8, cur_out_sig); |
| assert(in_loc->kind==LLK_Stk && out_loc->kind==LLK_Stk); |
| add_imm(tmp_addr1, sp, in_loc->addr); |
| add_imm(tmp_addr2, sp, out_loc->addr); |
| for(; i<n_inputs; i++) { |
| emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp_res->addr, tmp_addr1->addr, 8, current_predicate); |
| emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, tmp_addr2->addr, tmp_res->addr, 8, current_predicate); |
| } |
| } // do_in_to_out |
| |
| |
| // sets up the stack frame |
| void set_stk_frame() |
| { |
| unsigned stk_size = context.get_stk_size(); |
| if (stk_size != 0) { |
| add_imm(sp, sp, -(int)stk_size); |
| } |
| } // set_stk_frame |
| |
| // unsets the stack frame |
| void unset_stk_frame() |
| { |
| unsigned stk_size = context.get_stk_size(); |
| if (stk_size != 0) { |
| add_imm(sp, sp, (int)stk_size); |
| } |
| } // unset_stk_frame |
| |
| // moves FR inputs, originally residing in f8-f15, into the stack |
| void move_FR_inputs() |
| { |
| if (context.must_move_fr_inputs()) { |
| unsigned n_fr_inputs = context.get_num_fr_inputs(); |
| int start_offset = context.get_input_save_offset(); |
| |
| // using the same address reg in all stores will create too many dependencies; |
| // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP |
| add_imm(tmp_addr1, sp, start_offset); |
| if (n_fr_inputs >=2) |
| add_imm(tmp_addr2, sp, start_offset+16); |
| |
| for (unsigned i=0; i < n_fr_inputs; i++) { |
| unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; |
| emitter.ipf_stf_inc_imm(float_mem_size_d, mem_st_spill, mem_none, |
| addr_reg, 8+i, 32, current_predicate); |
| } |
| } |
| } // move_FR_inputs |
| |
| // moves FR inputs from the stack back to f8-f15 |
| void unmove_FR_inputs() |
| { |
| if (context.must_move_fr_inputs()) { |
| unsigned n_fr_inputs = context.get_num_fr_inputs(); |
| int start_offset = context.get_input_save_offset(); |
| |
| // using the same address reg in all stores will create too many dependencies; |
| // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP |
| add_imm(tmp_addr1, sp, start_offset); |
| if (n_fr_inputs >=2) |
| add_imm(tmp_addr2, sp, start_offset+16); |
| |
| for (unsigned i=0; i < n_fr_inputs; i++) { |
| unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; |
| emitter.ipf_ldf_inc_imm(float_mem_size_d, mem_ld_fill, mem_none, 8+i, addr_reg, 32, current_predicate); |
| } |
| } |
| } // unmove_FR_inputs |
| |
| |
| // emits the code that needs to go at the very beginning of the code stub |
| void do_entry() |
| { |
| assert(current_predicate==0); |
| |
| // start with an alloc instruction |
| const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); |
| if (pfs_save_gr) { |
| emitter.ipf_alloc(pfs_save_gr->addr, |
| context.get_input_space_size(), |
| context.get_local_space_size(), |
| context.get_output_space_size(), |
| 0); // no rotating regs! |
| } |
| |
| // save b0 |
| const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); |
| if (return_save_gr) { |
| emitter.ipf_mfbr(return_save_gr->addr, BRANCH_RETURN_LINK_REG); |
| } |
| |
| // save gp |
| const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); |
| if (gp_save_gr) |
| emitter.ipf_mov(gp_save_gr->addr, GP_REG); |
| |
| set_stk_frame(); |
| move_FR_inputs(); |
| } // do_entry |
| |
| |
| |
| /********************** |
| * visitor functions |
| */ |
| |
| unsigned current_predicate; // Predicate all instructions with this instruction (may not work for all instructions) |
| |
| public: |
| |
| void ret() |
| { |
| unset_stk_frame(); |
| |
| // restore gp |
| const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); |
| if (gp_save_gr) |
| emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); |
| |
| // restore b0 |
| const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); |
| if (return_save_gr) { |
| emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr, current_predicate); |
| } |
| |
| // restore ar.pfs |
| const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); |
| if (pfs_save_gr) |
| emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr, current_predicate); |
| |
| // if this ret is going back to LIL or managed code, reset ar.ccv |
| LilCc call_conv = lil_sig_get_cc(context.get_entry_sig()); |
| if (context.must_reset_ccv() && |
| (call_conv == LCC_Managed || |
| call_conv == LCC_Rth)) { |
| emitter.ipf_mtap(AR_ccv, 0, current_predicate); |
| } |
| |
| // return |
| emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, current_predicate); |
| } // ret |
| |
| |
| void label(LilLabel lab) { |
| emitter.set_target(context.get_label_id(lab)); |
| } // label |
| |
| |
| void locals(unsigned) { |
| // nothing to be done here; |
| // has been taken care of in the prepass |
| } // locals |
| |
| |
| void std_places(unsigned) { |
| // nothing to be done here; |
| // has been taken care of in the prepass |
| } // std_places |
| |
| void alloc(LilVariable* var, unsigned sz) { |
| // the actual size allocated will always be a multiple of 8 |
| sz = (sz + 0x7) & ~0x7; |
| int alloc_offset = context.get_alloc_start_offset() + current_alloc; |
| current_alloc += sz; |
| |
| // var = sp + alloc_offset |
| const LcgIpfLoc* var_loc = |
| context.get_var_loc(var, inst, ic, true); |
| add_imm(var_loc, sp, alloc_offset); |
| } // alloc |
| |
| |
| void asgn(LilVariable* dest, enum LilOperation o, LilOperand* op1, LilOperand*op2) |
| { |
| const LcgIpfLoc* dest_loc = |
| context.get_var_loc(dest, inst, ic, true); |
| |
| if (o == LO_Mov) { |
| if (lil_operand_is_immed(op1)) { |
| move_imm(dest_loc, lil_operand_get_immed(op1)); |
| } |
| else { |
| const LcgIpfLoc* src_loc = |
| context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); |
| move(dest_loc, src_loc); |
| } |
| } |
| else if (lil_operation_is_binary(o)) { |
| if (lil_operand_is_immed(op1) && lil_operand_is_immed(op2)) { |
| // type-convert to get signed types of same length |
| int64 op1_imm = (int64) lil_operand_get_immed(op1); |
| int64 op2_imm = (int64) lil_operand_get_immed(op2); |
| POINTER_SIZE_INT result = 0; |
| switch (o) { |
| case LO_Add: |
| result = op1_imm + op2_imm; |
| break; |
| case LO_Sub: |
| result = op1_imm - op2_imm; |
| break; |
| case LO_SgMul: |
| result = op1_imm * op2_imm; |
| break; |
| case LO_Shl: |
| result = op1_imm << op2_imm; |
| break; |
| default: |
| DIE(("Unexpected LIL operation")); // control should never reach this point |
| } |
| move_imm(dest_loc, result); |
| } |
| else if (lil_operand_is_immed(op1)) { |
| const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); |
| switch (o) { |
| case LO_Add: |
| bin_op_imm(LO_Add, dest_loc, src2_loc, lil_operand_get_immed(op1)); |
| break; |
| case LO_Sub: |
| sub_op_imm(dest_loc, lil_operand_get_immed(op1), src2_loc); |
| break; |
| case LO_SgMul: |
| case LO_Shl: |
| move_imm(tmp_res, (int64) lil_operand_get_immed(op1)); |
| bin_op(o, dest_loc, tmp_res, src2_loc); |
| break; |
| default: |
| DIE(("Unexpected LIL operation")); // control should never reach this point |
| } |
| } |
| else if (lil_operand_is_immed(op2)) { |
| const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); |
| bin_op_imm(o, dest_loc, src1_loc, lil_operand_get_immed(op2)); |
| } |
| else { // both operands non-immediate |
| const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); |
| const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); |
| bin_op(o, dest_loc, src1_loc, src2_loc); |
| } |
| } |
| else { // unary operation |
| if (lil_operand_is_immed(op1)) { |
| int64 imm = (int64) lil_operand_get_immed(op1); // typecast to get signed type |
| int64 result = 0; |
| switch (o) { |
| case LO_Neg: |
| result = -imm; |
| break; |
| case LO_Not: |
| result = ~imm; |
| break; |
| case LO_Sx1: |
| result = (int64) (I_8) imm; |
| break; |
| case LO_Sx2: |
| result = (int64) (int16) imm; |
| break; |
| case LO_Sx4: |
| result = (int64) (I_32) imm; |
| break; |
| case LO_Zx1: |
| result = (int64) (uint64) (U_8) imm; |
| break; |
| case LO_Zx2: |
| result = (int64) (uint64) (uint16) imm; |
| break; |
| case LO_Zx4: |
| result = (int64) (uint64) (U_32) imm; |
| break; |
| default: |
| DIE(("Unexpected LIL operation")); // control should never reach this point |
| } |
| move_imm(dest_loc, result); |
| } |
| else { // non-immediate operand |
| const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); |
| un_op(o, dest_loc, src1_loc); |
| } |
| } |
| } // asgn |
| |
| |
| void ts(LilVariable* var) { |
| const LcgIpfLoc* var_loc = |
| context.get_var_loc(var, inst, ic, true); |
| move(var_loc, tp); |
| } // ts |
| |
| void handles(LilOperand* op) |
| { |
| assert(current_predicate==0); |
| |
| if (lil_operand_is_immed(op)) { |
| m2n_gen_set_local_handles_imm(&emitter, lil_operand_get_immed(op)); |
| } else { |
| const LcgIpfLoc* op_loc = context.get_var_loc(lil_operand_get_variable(op), inst, ic, false); |
| assert(op_loc->kind == LLK_Gr); |
| m2n_gen_set_local_handles(&emitter, op_loc->addr); |
| } |
| } // handles |
| |
| void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilLdX ext) |
| { |
| // form the address |
| const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); |
| |
| // decide the size of the load (1, 2, 4, or 8) |
| Int_Mem_Size size = int_mem_size_1; |
| Sxt_Size ext_size = sxt_size_invalid; |
| bool sxt = ext==LLX_Sign; |
| switch(t) { |
| case LT_G1: |
| size = int_mem_size_1; ext_size = sxt_size_1; break; |
| case LT_G2: |
| size = int_mem_size_2; ext_size = sxt_size_2; break; |
| case LT_G4: |
| size = int_mem_size_4; ext_size = sxt_size_4; break; |
| case LT_G8: |
| case LT_Ref: |
| case LT_PInt: |
| size = int_mem_size_8; sxt = false; break; |
| case LT_F4: |
| case LT_F8: |
| case LT_Void: |
| DIE(("Unexpected LIL type")); // types not allowed in loads / stores |
| default: |
| DIE(("Unknown LIL type")); // invalid value in type |
| } |
| |
| Ld_Flag ld_flag = mem_ld_none; |
| switch (acqrel) { |
| case LAR_Acquire: ld_flag = mem_ld_acq; break; |
| case LAR_Release: DIE(("Unexpected acqrel value")); break; |
| case LAR_None: break; |
| } |
| |
| const LcgIpfLoc* dst_loc = context.get_var_loc(dst, inst, ic, true); |
| if (dst_loc->kind == LLK_Stk) { |
| // load value into tmp_res |
| emitter.ipf_ld(size, ld_flag, mem_none, tmp_res->addr, addr_loc->addr, current_predicate); |
| if (sxt) |
| emitter.ipf_sxt(ext_size, tmp_res->addr, tmp_res->addr, current_predicate); |
| // store value into dst_loc |
| move(dst_loc, tmp_res); |
| } |
| else if (dst_loc->kind == LLK_Gr) { |
| // load value into dst_loc |
| emitter.ipf_ld(size, ld_flag, mem_none, dst_loc->addr, addr_loc->addr, current_predicate); |
| if (sxt) |
| emitter.ipf_sxt(ext_size, dst_loc->addr, dst_loc->addr, current_predicate); |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // dst_loc shouldn't be FR or STK16 |
| } |
| } // ld |
| |
| |
| void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilOperand* src) |
| { |
| // move the address into tmp_addr1 |
| const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); |
| |
| // decide the size of the store (1, 2, 4, or 8) |
| Int_Mem_Size size = int_mem_size_1; |
| switch(t) { |
| case LT_G1: |
| size = int_mem_size_1; break; |
| case LT_G2: |
| size = int_mem_size_2; break; |
| case LT_G4: |
| size = int_mem_size_4; break; |
| case LT_G8: |
| case LT_Ref: |
| case LT_PInt: |
| size = int_mem_size_8; break; |
| case LT_F4: |
| case LT_F8: |
| case LT_Void: |
| DIE(("Unexpected LIL type")); // types not allowed in loads / stores |
| default: |
| DIE(("Unknown LIL type")); // invalid value in type |
| } |
| |
| St_Flag st_flag = mem_st_none; |
| switch (acqrel) { |
| case LAR_Acquire: DIE(("Unexpected value of acqrel")); break; |
| case LAR_Release: st_flag = mem_st_rel; break; |
| case LAR_None: break; |
| } |
| |
| const LcgIpfLoc* src_loc = NULL; |
| if (lil_operand_is_immed(src)) { |
| // move source into temp reg |
| POINTER_SIZE_INT imm = lil_operand_get_immed(src); |
| if (imm == 0) { |
| //just use r0 |
| src_loc = r0; |
| } |
| else { |
| move_imm(tmp_op1, lil_operand_get_immed(src)); |
| src_loc = tmp_op1; |
| } |
| } |
| else { |
| LilVariable *src_var = lil_operand_get_variable(src); |
| src_loc = context.get_var_loc(src_var, inst, ic, false); |
| } |
| if (src_loc->kind == LLK_Stk) { |
| // load src_loc into tmp_res |
| move(tmp_res, src_loc); |
| // store tmp_res |
| emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, tmp_res->addr, current_predicate); |
| } |
| else if (src_loc->kind == LLK_Gr) { |
| // store src_loc |
| emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, src_loc->addr, current_predicate); |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // src_loc shouldn't be FR or STK16! |
| } |
| } // st |
| |
| |
| void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel) |
| { |
| assert(acqrel==LAR_None); |
| |
| const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); |
| // load addr1 into tmp_res |
| emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, |
| tmp_res->addr, addr_loc->addr, current_predicate); |
| // inc tmp_res |
| add_imm(tmp_res, tmp_res, 1); |
| // store tmp_res back to addr1 |
| emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, |
| addr_loc->addr, tmp_res->addr, current_predicate); |
| } // inc |
| |
| void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, |
| LilOperand* cmp, LilOperand* src, LilLabel label) |
| { |
| assert(current_predicate==0); |
| |
| // move the address into tmp_addr1 |
| const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); |
| |
| // decide the size of the store (1, 2, 4, or 8) |
| Int_Mem_Size size = int_mem_size_1; |
| switch(t) { |
| case LT_G1: |
| size = int_mem_size_1; break; |
| case LT_G2: |
| size = int_mem_size_2; break; |
| case LT_G4: |
| size = int_mem_size_4; break; |
| case LT_G8: |
| case LT_Ref: |
| case LT_PInt: |
| size = int_mem_size_8; break; |
| case LT_F4: |
| case LT_F8: |
| case LT_Void: |
| DIE(("Unexpected LIL type")); // types not allowed in loads / stores |
| default: |
| DIE(("Unknown LIL type")); // invalid value in type |
| } |
| |
| Cmpxchg_Flag flag = mem_cmpxchg_acq; |
| switch (acqrel) { |
| case LAR_Acquire: flag = mem_cmpxchg_acq; break; |
| case LAR_Release: flag = mem_cmpxchg_rel; break; |
| default: |
| DIE(("Unexpected value of acqrel")); |
| } |
| |
| const LcgIpfLoc* src_loc = NULL; |
| if (lil_operand_is_immed(src)) { |
| // move source into temp reg |
| POINTER_SIZE_INT imm = lil_operand_get_immed(src); |
| if (imm == 0) { |
| //just use r0 |
| src_loc = r0; |
| } |
| else { |
| move_imm(tmp_op1, lil_operand_get_immed(src)); |
| src_loc = tmp_op1; |
| } |
| } |
| else { |
| LilVariable *src_var = lil_operand_get_variable(src); |
| src_loc = context.get_var_loc(src_var, inst, ic, false); |
| if (src_loc->kind == LLK_Stk) { |
| move(tmp_op1, src_loc); |
| src_loc = tmp_op1; |
| } |
| } |
| |
| const LcgIpfLoc* cmp_loc = NULL; |
| if (lil_operand_is_immed(cmp)) { |
| // move source into temp reg |
| POINTER_SIZE_INT imm = lil_operand_get_immed(cmp); |
| if (imm == 0) { |
| //just use r0 |
| cmp_loc = r0; |
| } |
| else { |
| move_imm(tmp_op2, lil_operand_get_immed(cmp)); |
| cmp_loc = tmp_op2; |
| } |
| } |
| else { |
| LilVariable *cmp_var = lil_operand_get_variable(cmp); |
| cmp_loc = context.get_var_loc(cmp_var, inst, ic, false); |
| if (cmp_loc->kind == LLK_Stk) { |
| move(tmp_op2, cmp_loc); |
| cmp_loc = tmp_op2; |
| } |
| } |
| |
| emitter.ipf_mtap(AR_ccv, cmp_loc->addr, current_predicate); |
| emitter.ipf_cmpxchg(size, flag, mem_none, tmp_res->addr, addr_loc->addr, src_loc->addr, current_predicate); |
| emitter.ipf_mtap(AR_ccv, 0, current_predicate); |
| emitter.ipf_cmp(icmp_ne, cmp_unc, tmp_pred, 0, tmp_res->addr, cmp_loc->addr, false, 0); |
| unsigned label_id = context.get_label_id(label); |
| emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); |
| } |
| |
| void j(LilLabel lab) |
| { |
| unsigned label_id = context.get_label_id(lab); |
| emitter.ipf_br(br_cond, br_few, br_sptk, br_none, label_id, current_predicate); |
| } // j |
| |
| void jc(LilPredicate p, LilOperand* op1, LilOperand* op2, LilLabel label) |
| { |
| assert(current_predicate==0); |
| |
| // compute the condition |
| do_cond(p, op1, op2, tmp_pred, 0); |
| |
| // the actual branch |
| unsigned label_id = context.get_label_id(label); |
| emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); |
| } // jc |
| |
| void out(LilSig* sig) |
| { |
| cur_out_sig = sig; |
| // nothing else to do; space has been reserved already |
| } // out |
| |
| void in2out(LilSig* sig) |
| { |
| assert(!context.entry_sig_is_arbitrary()); |
| cur_out_sig = sig; |
| do_in_to_out(); |
| } // in2out |
| |
| |
| void call(LilOperand* target, LilCallKind kind) |
| { |
| assert(current_predicate==0); |
| |
| LilSig *out_sig = lil_ic_get_out_sig(ic); |
| LilCc call_conv = (kind == LCK_TailCall) ? lil_sig_get_cc(context.get_entry_sig()) : |
| lil_sig_get_cc(out_sig); |
| |
| // reset ar.ccv if this call could lead to managed or LIL code |
| if (context.must_reset_ccv() && |
| (call_conv == LCC_Managed || |
| call_conv == LCC_Rth)) { |
| emitter.ipf_mtap(AR_ccv, 0); |
| } |
| |
| if (kind == LCK_TailCall) { |
| // move FR inputs to their original place |
| unmove_FR_inputs(); |
| |
| // unset stack, so that old stacked arguments become visible |
| unset_stk_frame(); |
| |
| // restore gp |
| const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); |
| if (gp_save_gr) |
| emitter.ipf_mov(GP_REG, gp_save_gr->addr); |
| |
| // restore b0 |
| const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); |
| if (return_save_gr) { |
| emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr); |
| } |
| |
| // restore ar.pfs |
| const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); |
| if (pfs_save_gr) |
| emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr); |
| |
| // jump (instead of calling) |
| if (lil_operand_is_immed(target)) { |
| void **proc_ptr = (void **) lil_operand_get_immed(target); |
| emit_branch_with_gp(emitter, proc_ptr); |
| } |
| else { |
| LilVariable *var = lil_operand_get_variable(target); |
| const LcgIpfLoc* loc = |
| context.get_var_loc(var, inst, ic, false); |
| unsigned call_addr_gr = 0; |
| if (loc->kind == LLK_Gr) { |
| call_addr_gr = loc->addr; |
| } |
| else if (loc->kind == LLK_Stk) { |
| // load loc into tmp_res |
| move(tmp_res, loc); |
| call_addr_gr = tmp_res->addr; |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // address can't be FP! |
| } |
| emitter.ipf_mtbr(tmp_br, call_addr_gr); |
| emitter.ipf_bri(br_cond, br_many, br_sptk, br_none, tmp_br); |
| } |
| |
| return; |
| } // kind == LCK_TailCall |
| |
| // kind == LCK_Call or kind == LCK_CallNoRet |
| |
| if (lil_operand_is_immed(target)) { |
| void** proc_ptr = (void **) lil_operand_get_immed(target); |
| void* fn_addr = proc_ptr[0]; |
| void* gp_new = proc_ptr[1]; |
| void* gp_old = get_vm_gp_value(); |
| if (gp_new != gp_old) { |
| // Set new gp |
| emit_mov_imm_compactor(emitter, GP_REG, (uint64)gp_new); |
| } |
| emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)fn_addr, 0); |
| if (context.has_push_m2n()) { |
| emitter.ipf_mtbr(BRANCH_CALL_REG, tmp_res->addr); |
| emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)m2n_gen_flush_and_call(), 0); |
| } |
| emitter.ipf_mtbr(tmp_br, tmp_res->addr); |
| emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); |
| if (gp_new != gp_old) { |
| // Restore gp |
| const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); |
| assert(gp_save_gr); |
| emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); |
| } |
| } |
| else { |
| LilVariable *var = lil_operand_get_variable(target); |
| const LcgIpfLoc* loc = |
| context.get_var_loc(var, inst, ic, false); |
| unsigned call_addr_gr = 0; |
| if (loc->kind == LLK_Gr) { |
| call_addr_gr = loc->addr; |
| } |
| else if (loc->kind == LLK_Stk) { |
| // load loc into tmp_res |
| move(tmp_res, loc); |
| call_addr_gr = tmp_res->addr; |
| } |
| else { |
| LDIE(73, "Unexpected kind"); // address can't be FP! |
| } |
| if (context.has_push_m2n()) { |
| emitter.ipf_mtbr(BRANCH_CALL_REG, call_addr_gr); |
| emit_mov_imm_compactor(emitter, call_addr_gr, (uint64)m2n_gen_flush_and_call(), 0); |
| } |
| emitter.ipf_mtbr(tmp_br, call_addr_gr); |
| emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); |
| } |
| } // call |
| |
| |
| void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) |
| { |
| assert(current_predicate==0); |
| m2n_gen_push_m2n(&emitter, method, current_frame_type, handles, context.get_stk_size(), 0, 0, false); |
| } // push_m2n |
| |
| void m2n_save_all() |
| { |
| assert(current_predicate==0); |
| add_imm(sp, sp, context.get_extra_saves_start_offset()+M2N_EXTRA_SAVES_SPACE-16); |
| m2n_gen_save_extra_preserved_registers(&emitter); |
| add_imm(sp, sp, -(int64)(context.get_extra_saves_start_offset()-16)); |
| } // m2n_save_all |
| |
| void pop_m2n() |
| { |
| assert(current_predicate==0); |
| // see if handles are present |
| bool handles = lil_ic_get_m2n_state(ic) == LMS_Handles; |
| int target = -1; |
| if (handles) |
| // this pop_m2n will need to use an emitter target |
| target = (int) context.get_unused_target(); |
| M2nPreserveRet pr; |
| LilType rt = lil_ic_get_ret_type(ic); |
| if (rt==LT_Void) |
| pr = MPR_None; |
| else if (rt==LT_F4 || rt==LT_F8) |
| pr = MPR_Fr; |
| else |
| pr = MPR_Gr; |
| m2n_gen_pop_m2n(&emitter, handles, pr, false, context.get_first_output_gr(), target); |
| } // pop_m2n |
| |
| void print(char *str, LilOperand *o) |
| { |
| assert(current_predicate==0); |
| |
| // this is a no-op if debugging is off |
| #ifdef STUB_DEBUG |
| unsigned print_reg; |
| if (lil_operand_is_immed(o)) { |
| // dummy operand; print r0 |
| print_reg = 0; |
| } |
| else { |
| LilVariable *var = lil_operand_get_variable(o); |
| const LcgIpfLoc* var_loc = |
| context.get_var_loc(var, inst, ic, false); |
| assert(var_loc->kind == LLK_Gr); |
| print_reg = var_loc->addr; |
| } |
| emit_print_reg(emitter, str, print_reg, context.get_num_inputs(), |
| context.get_first_output_gr(), false); |
| #endif // STUB_DEBUG |
| } // print |
| |
| public: |
| /****************************** |
| * constructors and destructors |
| */ |
| void *operator new(size_t sz, tl::MemoryPool &m) { |
| return m.alloc(sz); |
| } |
| |
| |
| LcgIpfCodeGen(LilCodeStub* cs, Merced_Code_Emitter& e, LcgIpfContext& c, tl::MemoryPool& m): |
| cs(cs), |
| emitter(e), |
| context(c), |
| mem(m), |
| iter(cs, true), |
| ic(NULL), |
| current_alloc(0), |
| cur_out_sig(NULL), |
| current_predicate(0) |
| { |
| // emit entry code |
| do_entry(); |
| |
| while (!iter.at_end()) { |
| ic = iter.get_context(); |
| inst = iter.get_current(); |
| lil_visit_instruction(inst, this); |
| iter.goto_next(); |
| } |
| } // LcgIpfCodeGen (constructor) |
| |
| }; // class LcgIpfCodeGen |
| |
| |
| // initialization of static members |
| // TODO: we have to get rid of memory pool in static area due to initialization problems |
| static tl::MemoryPool loc_mem; |
| const LcgIpfLoc* LcgIpfCodeGen::gp = new(loc_mem) LcgIpfLoc(LLK_Gr, 1); |
| const LcgIpfLoc* LcgIpfCodeGen::sp = new(loc_mem) LcgIpfLoc(LLK_Gr, 12); |
| const LcgIpfLoc* LcgIpfCodeGen::r0 = new(loc_mem) LcgIpfLoc(LLK_Gr, 0); |
| const LcgIpfLoc* LcgIpfCodeGen::tp = new(loc_mem) LcgIpfLoc(LLK_Gr, 4); // thread pointer is in r4 |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_op1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 31); // r30 and r31 used as temp operand locations |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_op2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 30); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_res = new(loc_mem) LcgIpfLoc(LLK_Gr, 29); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_f = new(loc_mem) LcgIpfLoc(LLK_Fr, 6); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_addr1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 28); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_addr2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 27); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_addr3 = new(loc_mem) LcgIpfLoc(LLK_Gr, 26); |
| const LcgIpfLoc* LcgIpfCodeGen::tmp_addl = new(loc_mem) LcgIpfLoc(LLK_Gr, 3); |
| const unsigned LcgIpfCodeGen::tmp_pred = 6; // use p6 (scratch) for any conditional jumps |
| const unsigned LcgIpfCodeGen::tmp_br = 6; //use BR 6 as a temporary branch address register |
| |
| |
| |
| LilCodeGeneratorIpf::LilCodeGeneratorIpf() |
| : LilCodeGenerator() |
| { |
| } |
| |
| NativeCodePtr LilCodeGeneratorIpf::compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) { |
| |
| // start a memory manager |
| tl::MemoryPool m; |
| |
| // get context info and do a prepass |
| LcgIpfContext context(cs, m); |
| |
| // initiate an IPF code emitter |
| Merced_Code_Emitter emitter(m, 100, context.get_num_targets()); |
| emitter.memory_type_is_unknown(); // fix later |
| emitter.disallow_instruction_exchange(); |
| |
| LcgIpfCodeGen codegen(cs, emitter, context, m); |
| |
| // get the goodies from the emitter |
| emitter.flush_buffer(); |
| *stub_size = emitter.get_size(); |
| NativeCodePtr buffer = allocate_memory(*stub_size, code_pool); |
| emitter.copy((char*)buffer); |
| flush_hw_cache((U_8*)buffer, *stub_size); |
| sync_i_cache(); |
| |
| return buffer; |
| } // compile_main |
| |
| GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) |
| { |
| void** p = (void**)STD_MALLOC(2*sizeof(void*)); |
| assert(p); |
| p[0] = ncp; |
| p[1] = get_vm_gp_value(); |
| |
| return (GenericFunctionPointer)p; |
| } // lil_npc_to_fp |