Commit 1dbd1d9b authored by Yi Lin's avatar Yi Lin

Merge branch 'domtree' into 'develop'

multiple fixes and improvements

See merge request !46
parents 49182e46 c9de0874
......@@ -65,6 +65,18 @@ testmuc:test_swapstack:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_thread_and_stack.py -v
testmuc:test_cmp:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_cmp.py -v
testmuc:test_binop:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_binop.py -v
testjit:milestones:
stage: test
script:
......
......@@ -441,6 +441,15 @@ impl MuType {
}
}
/// gets the function signature for FuncRef or UFuncPtr, return None if the type is not
/// those two types
pub fn get_func_sig(&self) -> Option<P<MuFuncSig>> {
match self.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => Some(sig.clone()),
_ => None
}
}
/// gets the length (in bit) of a integer/pointer type (assume pointer types are always 64 bits)
// FIXME: should deprecate this function, and get the length from BackendType
pub fn get_int_length(&self) -> Option<usize> {
......
......@@ -4743,15 +4743,12 @@ impl<'a> InstructionSelection {
let ref ops = inst.ops;
let ref func = ops[calldata.func];
let ref func_sig = match func.v {
TreeNode_::Value(ref pv) => {
let ty: &MuType = &pv.ty;
match ty.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig,
_ => panic!("expected funcref/ptr type")
}
let func_sig = {
let t = func.ty();
match t.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig.clone(),
_ => panic!("expected funcref/ufuncptr type")
}
_ => panic!("expected funcref/ptr type")
};
debug_assert!(func_sig.arg_tys.len() == calldata.args.len());
......
......@@ -1731,17 +1731,17 @@ impl ASMCodeGen {
let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {} {}", inst, op1, op2);
let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1);
let (reg, id1, loc1) = self.prepare_reg(op1, inst.len() + 1 + mem.len() + 1);
let (reg, id, loc) = self.prepare_reg(op1, inst.len() + 1);
let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1 + reg.len() + 1);
if uses.contains_key(&id1) {
let mut locs = uses.get_mut(&id1).unwrap();
vec_utils::add_unique(locs, loc1.clone());
if uses.contains_key(&id) {
let mut locs = uses.get_mut(&id).unwrap();
vec_utils::add_unique(locs, loc.clone());
} else {
uses.insert(id1, vec![loc1.clone()]);
uses.insert(id, vec![loc.clone()]);
}
let asm = format!("{} {},{}", inst, mem, reg);
let asm = format!("{} {},{}", inst, reg, mem);
self.add_asm_inst(asm, linked_hashmap!{}, uses, true)
}
......@@ -1832,10 +1832,7 @@ impl ASMCodeGen {
/// emits an instruction (use 1 reg 1 mem, define the reg)
fn internal_binop_def_r_mem(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) {
let len = match dest.ty.get_int_length() {
Some(n) if n == 64 | 32 | 16 | 8 => n,
_ => panic!("unimplemented int types: {}", dest.ty)
};
let len = check_op_len(dest);
let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {}, {} -> {}", inst, src, dest, dest);
......@@ -2256,10 +2253,26 @@ impl ASMCodeGen {
)
}
/// emits an instruction (use 2 fpregs, define 1st fpreg)
fn internal_fp_binop_def_r_mem(&mut self, inst: &str, dest: Reg, src: Reg) {
/// emits an instruction (use 1 fpreg 1 memory operand, define the fpreg)
fn internal_fp_binop_def_r_mem(&mut self, inst: &str, dest: Reg, src: Mem) {
trace!("emit: {} {}, {} -> {}", inst, src, dest, dest);
unimplemented!()
let (mem, mut uses) = self.prepare_mem(src, inst.len() + 1);
let (reg, id, loc) = self.prepare_fpreg(dest, inst.len() + 1 + mem.len() + 1);
// uses are GPRs, it won't include FPRs - we can simply insert into the map
uses.insert(id, vec![loc.clone()]);
let asm = format!("{} {},{}", inst, mem, reg);
self.add_asm_inst(
asm,
linked_hashmap!{
id => vec![loc]
},
uses,
true
)
}
/// emits a move instruction (reg -> fpreg)
......@@ -2523,6 +2536,10 @@ impl CodeGenerator for ASMCodeGen {
self.internal_binop_no_def_mem_r("cmp", op1, op2)
}
fn emit_cmp_r_mem(&mut self, op1: &P<Value>, op2: &P<Value>) {
self.internal_binop_no_def_r_mem("cmp", op1, op2)
}
fn emit_test_r_r(&mut self, op1: &P<Value>, op2: &P<Value>) {
self.internal_binop_no_def_r_r("test", op1, op2)
}
......@@ -4464,7 +4481,12 @@ pub fn spill_rewrite(
.clone_value();
// maintain mapping
trace!("reg {} used in Inst{} is replaced as {}", val_reg, i, temp);
trace!(
"reg {} used in Inst{} is replaced as {}",
val_reg.id(),
i,
temp
);
spilled_scratch_temps.insert(temp.id(), reg);
// generate a load
......@@ -4521,7 +4543,7 @@ pub fn spill_rewrite(
};
trace!(
"reg {} defined in Inst{} is replaced as {}",
val_reg,
val_reg.id(),
i,
temp
);
......
......@@ -61,7 +61,8 @@ pub trait CodeGenerator {
// comparison
fn emit_cmp_r_r(&mut self, op1: Reg, op2: Reg);
fn emit_cmp_imm_r(&mut self, op1: i32, op2: Reg);
fn emit_cmp_mem_r(&mut self, op1: Reg, op2: Reg);
fn emit_cmp_mem_r(&mut self, op1: Mem, op2: Reg);
fn emit_cmp_r_mem(&mut self, op1: Reg, op2: Mem);
fn emit_test_r_r(&mut self, op1: Reg, op2: Reg);
fn emit_test_imm_r(&mut self, op1: i32, op2: Reg);
......
......@@ -931,7 +931,7 @@ impl<'a> InstructionSelection {
let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
} else if self.match_mem(op) {
let mem_op = self.emit_mem(op, f_context, vm);
let mem_op = self.emit_mem(op, f_content, f_context, vm);
self.backend.emit_lea_r64(&tmp_res, &mem_op);
} else {
panic!("unexpected op (expect ireg): {}", op)
......@@ -2475,7 +2475,7 @@ impl<'a> InstructionSelection {
trace!("emit add-ireg-mem");
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let reg_op2 = self.emit_mem(op2, f_context, vm);
let reg_op2 = self.emit_mem(op2, f_content, f_context, vm);
// mov op1, res
self.backend.emit_mov_r_r(&res_tmp, &reg_op1);
......@@ -2537,7 +2537,7 @@ impl<'a> InstructionSelection {
trace!("emit sub-ireg-mem");
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
// mov op1, res
self.backend.emit_mov_r_r(&res_tmp, &reg_op1);
......@@ -2599,7 +2599,7 @@ impl<'a> InstructionSelection {
trace!("emit and-ireg-mem");
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
// mov op1, res
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
......@@ -2662,7 +2662,7 @@ impl<'a> InstructionSelection {
trace!("emit or-ireg-mem");
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
// mov op1, res
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
......@@ -2718,7 +2718,7 @@ impl<'a> InstructionSelection {
trace!("emit xor-ireg-mem");
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
// mov op1, res
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
......@@ -2765,6 +2765,13 @@ impl<'a> InstructionSelection {
// MUL with one is the original value
trace!("emit mul-ireg-1");
self.emit_move_node_to_value(&res_tmp, op1, f_content, f_context, vm);
} else if self.match_ireg(op1) && self.match_iconst_p2(op2) {
// MUL with a constant that is a power of 2 can be done with shl
trace!("emit mul-ireg-p2");
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let shift = self.node_iconst_to_p2(op2);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_shl_r_imm8(&res_tmp, shift as i8);
} else if self.match_ireg_ex(op1) && self.match_iconst_zero(op2) {
// MUL with zero is zero
trace!("emit mul-iregex-0");
......@@ -2875,16 +2882,25 @@ impl<'a> InstructionSelection {
match op_size {
1 | 2 | 4 | 8 => {
self.emit_udiv(op1, op2, f_content, f_context, vm);
if self.match_iconst_p2(op2) {
// we can simply logic shift right
let shift = self.node_iconst_to_p2(op2);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_shr_r_imm8(&res_tmp, shift as i8);
} else {
self.emit_udiv(op1, op2, f_content, f_context, vm);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
}
}
}
16 => {
......@@ -2913,16 +2929,25 @@ impl<'a> InstructionSelection {
match op_size {
1 | 2 | 4 | 8 => {
self.emit_idiv(op1, op2, f_content, f_context, vm);
if self.match_iconst_p2(op2) {
// we can simply arithmetic shift right
let shift = self.node_iconst_to_p2(op2);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_sar_r_imm8(&res_tmp, shift as i8);
} else {
self.emit_idiv(op1, op2, f_content, f_context, vm);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
}
}
}
16 => {
......@@ -3256,7 +3281,7 @@ impl<'a> InstructionSelection {
trace!("emit add-fpreg-mem");
let reg_op1 = self.emit_fpreg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
match reg_op1.ty.v {
MuType_::Double => {
......@@ -3308,7 +3333,7 @@ impl<'a> InstructionSelection {
trace!("emit sub-fpreg-mem");
let reg_op1 = self.emit_fpreg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
match reg_op1.ty.v {
MuType_::Double => {
......@@ -3359,7 +3384,7 @@ impl<'a> InstructionSelection {
trace!("emit mul-fpreg-mem");
let reg_op1 = self.emit_fpreg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
match reg_op1.ty.v {
MuType_::Double => {
......@@ -3407,7 +3432,7 @@ impl<'a> InstructionSelection {
trace!("emit div-fpreg-mem");
let reg_op1 = self.emit_fpreg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
match reg_op1.ty.v {
MuType_::Double => {
......@@ -3837,7 +3862,7 @@ impl<'a> InstructionSelection {
// div op2
if self.match_mem(op2) {
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
self.backend.emit_div_mem(&mem_op2);
} else if self.match_iimm(op2) {
let imm = self.node_iimm_to_i32(op2);
......@@ -3903,7 +3928,7 @@ impl<'a> InstructionSelection {
// idiv op2
if self.match_mem(op2) {
let mem_op2 = self.emit_mem(op2, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
self.backend.emit_idiv_mem(&mem_op2);
} else if self.match_iimm(op2) {
let imm = self.node_iimm_to_i32(op2);
......@@ -4441,16 +4466,10 @@ impl<'a> InstructionSelection {
let ref ops = inst.ops;
let ref func = ops[calldata.func];
let ref func_sig = match func.v {
TreeNode_::Value(ref pv) => {
let ty: &MuType = &pv.ty;
match ty.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig,
_ => panic!("expected funcref/ptr type")
}
}
_ => {
// emit a funcref from an instruction
unimplemented!()
TreeNode_::Value(ref pv) => pv.ty.get_func_sig().unwrap(),
TreeNode_::Instruction(ref inst) => {
let ref funcref_val = inst.value.as_ref().unwrap()[0];
funcref_val.ty.get_func_sig().unwrap()
}
};
......@@ -4516,7 +4535,7 @@ impl<'a> InstructionSelection {
x86_64::ALL_CALLER_SAVED_REGS.to_vec()
)
} else if self.match_mem(func) {
let target = self.emit_mem(func, f_context, vm);
let target = self.emit_mem(func, f_content, f_context, vm);
let callsite = self.new_callsite_label(Some(node));
self.backend.emit_call_near_mem64(
......@@ -5105,6 +5124,20 @@ impl<'a> InstructionSelection {
self.backend.emit_cmp_imm_r(iimm_op2, &reg_op1);
return op;
} else if self.match_ireg(op1) && self.match_mem(op2) {
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let mem_op2 = self.emit_mem(op2, f_content, f_context, vm);
self.backend.emit_cmp_mem_r(&mem_op2, &reg_op1);
return op;
} else if self.match_mem(op1) && self.match_ireg(op2) {
let mem_op1 = self.emit_mem(op1, f_content, f_context, vm);
let reg_op2 = self.emit_ireg(op2, f_content, f_context, vm);
self.backend.emit_cmp_r_mem(&reg_op2, &mem_op1);
return op;
} else if self.match_ireg(op1) && self.match_ireg(op2) {
// comparing two iregs (general case)
......@@ -5297,6 +5330,10 @@ impl<'a> InstructionSelection {
Value_::SSAVar(_) | Value_::Constant(_) => {
RegGroup::get_from_value(&pv) == RegGroup::GPR
}
Value_::Global(_) => {
// global is always a ireg (it is iref<T>)
true
}
_ => false
}
}
......@@ -5557,6 +5594,33 @@ impl<'a> InstructionSelection {
}
}
/// matches an integer that is power of 2
fn match_iconst_p2(&mut self, op: &TreeNode) -> bool {
match op.v {
TreeNode_::Value(ref pv) if pv.is_const() => {
if pv.is_int_const() {
math::is_power_of_two(pv.extract_int_const().unwrap() as usize).is_some()
} else {
false
}
}
_ => false
}
}
fn node_iconst_to_p2(&mut self, op: &TreeNode) -> u8 {
match op.v {
TreeNode_::Value(ref pv) if pv.is_const() => {
if pv.is_int_const() {
math::is_power_of_two(pv.extract_int_const().unwrap() as usize).unwrap()
} else {
unreachable!()
}
}
_ => unreachable!()
}
}
/// matches a floatingpoint zero
fn match_fconst_zero(&mut self, op: &TreeNode) -> bool {
match op.v {
......@@ -5716,21 +5780,15 @@ impl<'a> InstructionSelection {
let ref ops = inst.ops;
match inst.v {
// GETIREF -> [base]
// GETIREF: returns operand
Instruction_::GetIRef(op_index) => {
trace!("MEM from GETIREF: {}", op);
let ref ref_op = ops[op_index];
let tmp_op = self.emit_ireg(ref_op, f_content, f_context, vm);
let ret = MemoryLocation::Address {
base: tmp_op,
offset: None,
index: None,
scale: None
};
trace!("MEM from GETIREF: {}", ret);
ret
self.emit_inst_addr_to_value_inner(ref_op, f_content, f_context, vm)
}
// GETFIELDIREF: adjust offset
Instruction_::GetFieldIRef { base, index, .. } => {
trace!("MEM from GETFIELDIREF: {}", op);
let ref base = ops[base];
let struct_ty = {
let ref iref_or_uptr_ty = base.as_value().ty;
......@@ -5747,40 +5805,12 @@ impl<'a> InstructionSelection {
};
let field_offset: i32 = self.get_field_offset(&struct_ty, index, vm);
match base.v {
// GETFIELDIREF(GETIREF) -> add FIELD_OFFSET to old offset
TreeNode_::Instruction(Instruction {
v: Instruction_::GetIRef(_),
..
}) => {
let mem = self.emit_inst_addr_to_value_inner(
base,
f_content,
f_context,
vm
);
let ret =
self.addr_const_offset_adjust(mem, field_offset as u64, vm);
trace!("MEM from GETFIELDIREF(GETIREF): {}", ret);
ret
}
// GETFIELDIREF(ireg) -> [base + FIELD_OFFSET]
_ => {
let tmp = self.emit_ireg(base, f_content, f_context, vm);
let ret = MemoryLocation::Address {
base: tmp,
offset: Some(self.make_int_const(field_offset as u64, vm)),
index: None,
scale: None
};
trace!("MEM from GETFIELDIREF(ireg): {}", ret);
ret
}
}
let m = self.emit_inst_addr_to_value_inner(base, f_content, f_context, vm);
self.addr_const_offset_adjust(m, field_offset as u64, vm)
}
// GETVARPARTIREF: adjust offset
Instruction_::GetVarPartIRef { base, .. } => {
trace!("MEM from GETVARPARTIREF: {}", op);
let ref base = ops[base];
let struct_ty = match base.as_value().ty.get_referent_ty() {
Some(ty) => ty,
......@@ -5788,260 +5818,71 @@ impl<'a> InstructionSelection {
};
let fix_part_size = vm.get_backend_type_size(struct_ty.id());
match base.v {
// GETVARPARTIREF(GETIREF) -> add FIX_PART_SIZE to old offset
TreeNode_::Instruction(Instruction {
v: Instruction_::GetIRef(_),
..
}) => {
let mem = self.emit_inst_addr_to_value_inner(
base,
f_content,
f_context,
vm
);
let ret =
self.addr_const_offset_adjust(mem, fix_part_size as u64, vm);
trace!("MEM from GETIVARPARTIREF(GETIREF): {}", ret);
ret
}
// GETVARPARTIREF(ireg) -> [base + VAR_PART_SIZE]
_ => {
let tmp = self.emit_ireg(base, f_content, f_context, vm);
let ret = MemoryLocation::Address {
base: tmp,
offset: Some(self.make_int_const(fix_part_size as u64, vm)),
index: None,
scale: None
};
trace!("MEM from GETVARPARTIREF(ireg): {}", ret);
ret
}
}
let m = self.emit_inst_addr_to_value_inner(base, f_content, f_context, vm);
self.addr_const_offset_adjust(m, fix_part_size as u64, vm)
}
Instruction_::ShiftIRef { base, offset, .. } => {
// SHIFTIREF:
// either adjust offset (if the index is constant), or use index/scale
Instruction_::ShiftIRef {
base,
offset: index,
..
} |
Instruction_::GetElementIRef { base, index, .. } => {
let ref base = ops[base];
let ref offset = ops[offset];
let ref index = ops[index];
let ref base_ty = base.clone_value().ty;
let ele_ty = match base_ty.get_referent_ty() {
let ref iref_ty = base.as_value().ty;
let base_ty = match iref_ty.get_referent_ty() {
Some(ty) => ty,
None => {
panic!(
"expected op in ShiftIRef of type IRef, found type: {}",
base_ty
iref_ty
)
}
};
let ele_backend_ty = vm.get_backend_type_info(ele_ty.id());
let ele_ty_size =
math::align_up(ele_backend_ty.size, ele_backend_ty.alignment);
if self.match_iimm(offset) {
// byte offset is known at compile time,
// we can calculate it as a constant offset
let index = self.node_iimm_to_i32(offset);
let shift_size = ele_ty_size as i32 * index;
let mem = match base.v {
// SHIFTIREF(GETVARPARTIREF(_), imm) -> add shift_size to old offset
TreeNode_::Instruction(Instruction {
v: Instruction_::GetVarPartIRef { .. },
..
}) => {
let mem = self.emit_inst_addr_to_value_inner(
base,
f_content,
f_context,
vm
);
let ret =
self.addr_const_offset_adjust(mem, shift_size as u64, vm);
trace!("MEM from SHIFTIREF(GETVARPARTIREF(_), imm): {}", ret);
ret
let ele_ty_size = {
let backend_ty = vm.get_backend_type_info(base_ty.id());
match inst.v {
Instruction_::ShiftIRef { .. } => {
math::align_up(backend_ty.size, backend_ty.alignment)
}
// SHIFTIREF(ireg, imm) -> [base + SHIFT_SIZE]
_ => {
let tmp = self.emit_ireg(base, f_content, f_context, vm);
let ret = MemoryLocation::Address {
base: tmp,
offset: Some(self.make_int_const(shift_size as u64, vm)),
index: None,
scale: None
};
trace!("MEM from SHIFTIREF(ireg, imm): {}", ret);
ret
Instruction_::GetElementIRef { .. } => {
backend_ty.elem_size.unwrap()
}
};
mem
} else {
// we need to calculate byte offset at runtime
let tmp_index = self.emit_ireg(offset, f_content, f_context, vm);
// make a copy of it
// (because we may need to alter index, and we dont want to change
// the original value)
let tmp_index_copy =
self.make_temporary(f_context, tmp_index.ty.clone(), vm);
self.emit_move_value_to_value(&tmp_index_copy, &tmp_index);
let scale: u8 = match ele_ty_size {
// if we can use x86 scale
8 | 4 | 2 | 1 => ele_ty_size as u8,
// if we can get byte offset by shifting
16 | 32 | 64 => {
let shift = math::is_power_of_two(ele_ty_size).unwrap();
// tmp_index_copy = tmp_index_copy << index
self.backend.emit_shl_r_imm8(&tmp_index_copy, shift as i8);
1
}
// otherwise we have to do multiplication
_ => {
// mov ele_ty_size -> rax
self.backend
.emit_mov_r_imm(&x86_64::RAX, ele_ty_size as i32);
// mul tmp_index_copy rax -> rdx:rax
self.backend.emit_mul_r(&tmp_index_copy);
// mov rax -> tmp_index_copy
self.backend.emit_mov_r_r(&tmp_index_copy, &x86_64::RAX);
1
}