diff --git a/examples/brainfuck.rs b/examples/brainfuck.rs index 80aba69..4a1866c 100644 --- a/examples/brainfuck.rs +++ b/examples/brainfuck.rs @@ -1,10 +1,8 @@ #![feature(plugin, custom_attribute)] #![plugin(holyjit_plugin)] #![feature(unboxed_closures)] - #[macro_use] extern crate holyjit_lib as hj; - jit!{ fn eval(jc: hj::JitContext, program: String) -> Result<(), ()> = eval_impl in jc; } fn eval_impl(_jc: hj::JitContext, program: String) -> Result<(), ()> { let prog = program.as_bytes(); @@ -19,8 +17,8 @@ fn eval_impl(_jc: hj::JitContext, program: String) -> Result<(), ()> { match prog[pc] { b'>' => { val += 1; } b'<' => { val -= 1; } - b'-' => { mem[val as usize] += 1; } - b'+' => { mem[val as usize] -= 1; } + b'-' => { mem[val as usize] -= 1; } + b'+' => { mem[val as usize] += 1; } b'.' => { panic!("putchar: NYI"); } b',' => { panic!("getchar: NYI"); } b'[' => { @@ -61,5 +59,6 @@ fn eval_impl(_jc: hj::JitContext, program: String) -> Result<(), ()> { fn main() { let jc : hj::JitContext = Default::default(); - eval(jc, "".into()).unwrap() + let res = eval(jc, "++".into()); + res.unwrap(); } diff --git a/lib/src/compile.rs b/lib/src/compile.rs index 7414486..c82b150 100644 --- a/lib/src/compile.rs +++ b/lib/src/compile.rs @@ -3,6 +3,7 @@ //! of concept, but is unlikely to be part of the final solution. use std::collections::HashMap; +use std::mem; use dynasmrt; use dynasmrt::x64; @@ -23,14 +24,15 @@ impl JitCode { /// Compile a function based on the vector of bytes and the table of /// statics. pub fn compile(bytes: &[u8], defs: *const ()) -> Result { - let c = Compiler::new(defs.clone()); - c.compile(bytes) + Compiler::compile(bytes, defs.clone()) } - /// Cast the current code into a fake class which implement the proper - /// trait. - pub fn get_fn<'ctx, Args, Output>(&self) -> &'ctx Fn { - panic!("NYI!!!") + /// Cast the current code into the proper fn-type given as parameter of + /// this generic function. + pub unsafe fn get_fn(&self) -> *const () { + unsafe { + mem::transmute(self.code.ptr(self.start)) + } } } @@ -43,6 +45,7 @@ pub enum Error { /// registers. This should be handled by a better register allocator in /// the future. NotEnoughRegisters, + NotEnoughRegistersForMovGrp, /// Cannot finalize the code, and generate an executable page. Finalize, @@ -50,8 +53,18 @@ pub enum Error { /// Cannot reuse deleted register mapping MissingRegisterMap, + /// We cannot find an allocation corresponding to a given register. + AllocatedButNotRegistered, + + /// We were not able to find the offset in which the data should be + /// stored into. + StoreIntoMissTarget(u8), + /// This is still a prototype. - NYI, + NYI(&'static str), + + /// + WhatTheHell } impl<'tcx> From> for Error { @@ -63,7 +76,7 @@ impl<'tcx> From> for Error { mod asm { use dynasmrt; - use dynasmrt::{DynasmApi, DynasmLabelApi}; + use dynasmrt::DynasmApi; #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u8)] @@ -76,6 +89,7 @@ mod asm { } // Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc). + /* #[derive(Copy, Clone, Debug)] #[repr(u8)] enum FpCondition { @@ -88,7 +102,9 @@ mod asm { Nle = 0x6, Ord = 0x7, } + */ + #[allow(unused)] #[derive(Copy, Clone, Debug)] #[repr(u8)] pub enum Condition { @@ -145,11 +161,13 @@ mod asm { // [2] http://ref.x86asm.net/geek.html // // OPn_NAME_DstSrc + #[allow(non_camel_case_types,unused)] // This is a prototype #[derive(Copy, Clone, Debug)] enum OneByteOpcodeID { OP_NOP_00, OP_ADD_EbGb, OP_ADD_EvGv, + OP_ADD_GbEb, OP_ADD_GvEv, OP_ADD_EAXIv, OP_OR_EbGb, @@ -167,6 +185,7 @@ mod asm { OP_AND_EAXIv, OP_SUB_EbGb, OP_SUB_EvGv, + OP_SUB_GbEb, OP_SUB_GvEv, OP_SUB_EAXIv, PRE_PREDICT_BRANCH_NOT_TAKEN, @@ -174,7 +193,9 @@ mod asm { OP_XOR_EvGv, OP_XOR_GvEv, OP_XOR_EAXIv, + OP_CMP_EbGb, OP_CMP_EvGv, + OP_CMP_GbEb, OP_CMP_GvEv, OP_CMP_EAXIv, PRE_REX, @@ -246,6 +267,7 @@ mod asm { OP_GROUP5_Ev } + #[allow(non_camel_case_types,unused)] // This is a prototype #[derive(Copy, Clone, Debug)] enum TwoByteOpcodeID { OP2_UD2, @@ -375,6 +397,7 @@ mod asm { OP2_PADDD_VdqWdq } + #[allow(non_camel_case_types,unused)] // This is a prototype #[derive(Copy, Clone, Debug)] enum ThreeByteOpcodeID { OP3_PSHUFB_VdqWdq, @@ -392,6 +415,8 @@ mod asm { OP3_VBLENDVPS_VdqWdq } + #[allow(non_camel_case_types,unused)] // This is a prototype + #[derive(Copy, Clone, Debug)] enum GroupOpcodeID { NOGROUP_OP_SETCC, @@ -444,6 +469,7 @@ mod asm { OP_NOP_00 => 0x00, OP_ADD_EbGb => 0x00, OP_ADD_EvGv => 0x01, + OP_ADD_GbEb => 0x02, OP_ADD_GvEv => 0x03, OP_ADD_EAXIv => 0x05, OP_OR_EbGb => 0x08, @@ -461,6 +487,7 @@ mod asm { OP_AND_EAXIv => 0x25, OP_SUB_EbGb => 0x28, OP_SUB_EvGv => 0x29, + OP_SUB_GbEb => 0x2A, OP_SUB_GvEv => 0x2B, OP_SUB_EAXIv => 0x2D, PRE_PREDICT_BRANCH_NOT_TAKEN => 0x2E, @@ -468,7 +495,9 @@ mod asm { OP_XOR_EvGv => 0x31, OP_XOR_GvEv => 0x33, OP_XOR_EAXIv => 0x35, + OP_CMP_EbGb => 0x38, OP_CMP_EvGv => 0x39, + OP_CMP_GbEb => 0x3A, OP_CMP_GvEv => 0x3B, OP_CMP_EAXIv => 0x3D, PRE_REX => 0x40, @@ -772,20 +801,31 @@ mod asm { pub backend: dynasmrt::x64::Assembler, } + // TODO: constant `hasSib` should have an upper case name such as `HAS_SIB` + #[allow(non_upper_case_globals)] const noBase : Register = Register::Rbp; + #[allow(non_upper_case_globals)] const hasSib : Register = Register::Rsp; + #[allow(non_upper_case_globals)] const noIndex : Register = Register::Rsp; + #[allow(non_upper_case_globals)] const noBase2 : Register = Register::R13; + #[allow(non_upper_case_globals)] const hasSib2 : Register = Register::R12; impl Assembler { - fn emit_rex(&mut self, w: bool, r: Register, x: Register, b: Register) { + fn emit_rex_u8(&mut self, w: bool, r: u8, x: u8, b: u8) { let w = w as u8; + let pre_rex : u8 = OneByteOpcodeID::PRE_REX.into(); + self.backend.push(pre_rex | (w << 3) | ((r >> 3) << 2) | ((x >> 3) << 1) | (b >> 3)); + } + + fn emit_rex(&mut self, w: bool, r: Register, x: Register, b: Register) { let r = r as u8; let x = x as u8; let b = b as u8; - self.backend.push(OneByteOpcodeID::PRE_REX as u8 | (w << 3) | ((r >> 3) << 2) | ((x >> 3) << 1) | (b >> 3)); + self.emit_rex_u8(w, r, x, b); } fn emit_rex_w(&mut self, r: Register, x: Register, b: Register) { self.emit_rex(true, r, x, b); @@ -814,7 +854,7 @@ mod asm { fn put_modrm_grp(&mut self, mode: ModRm, rm: Register, grp: GroupOpcodeID) { - self.put_modrm_u8(mode, rm as u8, grp as u8) + self.put_modrm_u8(mode, rm as u8, grp.into()) } fn put_modrm_sib(&mut self, mode: ModRm, base: Register, index: Register, scale: u8, reg: Register) @@ -860,12 +900,49 @@ mod asm { } } - fn one_op(&mut self, opcode: OneByteOpcodeID) { - self.backend.push(opcode.into()); + fn one_op8_reg(&mut self, opcode: OneByteOpcodeID, reg: Register) { + self.emit_rex_if(reg.byte_requires_rex(), Register::Rax, Register::Rax, reg); + let mut opcode : u8 = opcode.into(); + opcode += (reg as u8) & 0x7; + self.backend.push(opcode); } fn one_op_reg(&mut self, opcode: OneByteOpcodeID, reg: Register) { self.emit_rex_if_needed(Register::Rax, Register::Rax, reg); + let mut opcode : u8 = opcode.into(); + opcode += (reg as u8) & 0x7; + self.backend.push(opcode); + } + fn one_op64_reg(&mut self, opcode: OneByteOpcodeID, reg: Register) { + self.emit_rex_w(Register::Rax, Register::Rax, reg); + let mut opcode : u8 = opcode.into(); + opcode += (reg as u8) & 0x7; + self.backend.push(opcode) + } + + fn one_op_reg_grp(&mut self, opcode: OneByteOpcodeID, reg: Register, grp: GroupOpcodeID) { + { + let grp_u8 : u8 = grp.into(); + assert!(grp_u8 < 8); + } + self.emit_rex_if_needed(Register::Rax, Register::Rax, reg); + self.backend.push(opcode.into()); + self.put_modrm_grp(ModRm::Register, reg, grp); + } + fn one_op64_reg_grp(&mut self, opcode: OneByteOpcodeID, reg: Register, grp: GroupOpcodeID) { + { + let grp_u8 : u8 = grp.into(); + assert!(grp_u8 < 8); + } + self.emit_rex_w(Register::Rax, Register::Rax, reg); self.backend.push(opcode.into()); + self.put_modrm_grp(ModRm::Register, reg, grp); + } + + fn one_op8_rm_reg(&mut self, opcode: OneByteOpcodeID, rm: Register, reg: Register) { + self.emit_rex_if(reg.byte_requires_rex() || rm.byte_requires_rex(), + reg, Register::Rax, rm); + self.backend.push(opcode.into()); + self.register_modrm(rm, reg) } fn one_op_rm_reg(&mut self, opcode: OneByteOpcodeID, rm: Register, reg: Register) { self.emit_rex_if_needed(reg, Register::Rax, rm); @@ -877,6 +954,7 @@ mod asm { self.backend.push(opcode.into()); self.register_modrm(rm, reg) } + fn one_op8_mm_reg(&mut self, opcode: OneByteOpcodeID, base: Register, offset: i32, reg: Register) { self.emit_rex_if(reg.byte_requires_rex(), reg, Register::Rax, base); self.backend.push(opcode.into()); @@ -892,6 +970,7 @@ mod asm { self.backend.push(opcode.into()); self.memory_modrm(offset, base, reg) } + fn two_op8_cc_reg(&mut self, opcode: TwoByteOpcodeID, cond: Condition, rm: Register, grp: GroupOpcodeID) { self.emit_rex_if(rm.byte_requires_rex(), Register::Rax, Register::Rax, rm); self.backend.push(OneByteOpcodeID::OP_2BYTE_ESCAPE.into()); @@ -909,12 +988,53 @@ mod asm { // Set of Instructions used by the compiler. + pub fn andq_rr(&mut self, src: Register, dst: Register) { + self.one_op64_rm_reg(OneByteOpcodeID::OP_AND_GvEv, src, dst) + } + pub fn orq_rr(&mut self, src: Register, dst: Register) { + self.one_op64_rm_reg(OneByteOpcodeID::OP_OR_GvEv, src, dst) + } + pub fn xorq_rr(&mut self, src: Register, dst: Register) { + self.one_op64_rm_reg(OneByteOpcodeID::OP_XOR_GvEv, src, dst) + } + + pub fn addb_rr(&mut self, src: Register, dst: Register) { + self.one_op8_rm_reg(OneByteOpcodeID::OP_ADD_GbEb, src, dst) + } + pub fn addw_rr(&mut self, src: Register, dst: Register) { + self.backend.push(OneByteOpcodeID::PRE_OPERAND_SIZE.into()); + self.one_op_rm_reg(OneByteOpcodeID::OP_ADD_GvEv, src, dst) + } + pub fn addl_rr(&mut self, src: Register, dst: Register) { + self.one_op_rm_reg(OneByteOpcodeID::OP_ADD_GvEv, src, dst) + } pub fn addq_rr(&mut self, src: Register, dst: Register) { self.one_op64_rm_reg(OneByteOpcodeID::OP_ADD_GvEv, src, dst) } + + pub fn subb_rr(&mut self, src: Register, dst: Register) { + self.one_op8_rm_reg(OneByteOpcodeID::OP_SUB_GbEb, src, dst) + } + pub fn subw_rr(&mut self, src: Register, dst: Register) { + self.backend.push(OneByteOpcodeID::PRE_OPERAND_SIZE.into()); + self.one_op_rm_reg(OneByteOpcodeID::OP_SUB_GvEv, src, dst) + } + pub fn subl_rr(&mut self, src: Register, dst: Register) { + self.one_op_rm_reg(OneByteOpcodeID::OP_SUB_GvEv, src, dst) + } + pub fn subq_rr(&mut self, src: Register, dst: Register) { + self.one_op64_rm_reg(OneByteOpcodeID::OP_SUB_GvEv, src, dst) + } + pub fn imulq_rr(&mut self, src: Register, dst: Register) { self.two_op64_rm_reg(TwoByteOpcodeID::OP2_IMUL_GvEv, src, dst) } + + /// Compare 8 bits registers + pub fn cmpb_rr(&mut self, lhs: Register, rhs: Register) { + // Note, inverted arguments to make them logical. + self.one_op8_rm_reg(OneByteOpcodeID::OP_CMP_GbEb, rhs, lhs) + } /// Compare 16 bits registers pub fn cmpw_rr(&mut self, lhs: Register, rhs: Register) { // Note, inverted arguments to make them logical. @@ -931,6 +1051,29 @@ mod asm { // Note, inverted arguments to make them logical. self.one_op64_rm_reg(OneByteOpcodeID::OP_CMP_GvEv, rhs, lhs) } + + /// Copy 8 bits immediate + pub fn movb_ir(&mut self, value: i8, dst: Register) { + self.one_op8_reg(OneByteOpcodeID::OP_MOV_EbIb, dst); + self.backend.push_i8(value) + } + /// Copy 16 bits immediate + pub fn movw_ir(&mut self, value: i16, dst: Register) { + self.backend.push(OneByteOpcodeID::PRE_OPERAND_SIZE.into()); + self.one_op_reg(OneByteOpcodeID::OP_MOV_EAXIv, dst); + self.backend.push_i16(value) + } + /// Copy 32 bits immediate + pub fn movl_ir(&mut self, value: i32, dst: Register) { + self.one_op_reg(OneByteOpcodeID::OP_MOV_EAXIv, dst); + self.backend.push_i32(value) + } + /// Copy 64 bits immediate + pub fn movq_ir(&mut self, value: i64, dst: Register) { + self.one_op64_reg(OneByteOpcodeID::OP_MOV_EAXIv, dst); + self.backend.push_i64(value) + } + /// Load 8 bits pub fn movb_mr(&mut self, base: Register, offset: i32, dst: Register) { self.one_op8_mm_reg(OneByteOpcodeID::OP_MOV_GvEb, base, offset, dst) @@ -966,12 +1109,83 @@ mod asm { pub fn movq_rm(&mut self, src: Register, base: Register, offset: i32) { self.one_op64_mm_reg(OneByteOpcodeID::OP_MOV_EvGv, base, offset, src) } + + /// Copy 8 bits + pub fn movb_rr(&mut self, src: Register, dst: Register) { + self.one_op8_rm_reg(OneByteOpcodeID::OP_MOV_GvEb, src, dst) + } + /// Copy 16 bits + pub fn movw_rr(&mut self, src: Register, dst: Register) { + self.backend.push(OneByteOpcodeID::PRE_OPERAND_SIZE.into()); + self.one_op_rm_reg(OneByteOpcodeID::OP_MOV_GvEv, src, dst) + } + /// Copy 32 bits + pub fn movl_rr(&mut self, src: Register, dst: Register) { + self.one_op_rm_reg(OneByteOpcodeID::OP_MOV_GvEv, src, dst) + } + /// Copy 64 bits + pub fn movq_rr(&mut self, src: Register, dst: Register) { + self.one_op64_rm_reg(OneByteOpcodeID::OP_MOV_GvEv, src, dst) + } + /// Store condition flag in a 8 bits register, the upper bits of the /// destination register remain unchanged. pub fn setcc_r(&mut self, cc: Condition, out: Register) { - self.two_op8_cc_reg(TwoByteOpcodeID::OP_SETCC, cc, out, GroupOpcodeID::NOGROUP_OP_SETCC); + self.two_op8_cc_reg(TwoByteOpcodeID::OP_SETCC, cc, out, GroupOpcodeID::NOGROUP_OP_SETCC) + } + + /// Make a call to the address contained in the given register. + pub fn call_r(&mut self, dst: Register) { + self.one_op_reg_grp(OneByteOpcodeID::OP_GROUP5_Ev, dst, GroupOpcodeID::GROUP5_OP_CALLN) + } + + /// Arithmetic right shift. + pub fn sarq_ir(&mut self, imm: u8, dst: Register) { + match imm { + 0 => {} + 1 => self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_Ev1, dst, GroupOpcodeID::GROUP2_OP_SAR), + _ => { + self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_EvIb, dst, GroupOpcodeID::GROUP2_OP_SAR); + self.backend.push(imm); + } + } + } + /// Logical right shift + pub fn shrq_ir(&mut self, imm: u8, dst: Register) { + match imm { + 0 => {} + 1 => self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_Ev1, dst, GroupOpcodeID::GROUP2_OP_SHR), + _ => { + self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_EvIb, dst, GroupOpcodeID::GROUP2_OP_SHR); + self.backend.push(imm); + } + } + } + /// Logical left shift + pub fn shlq_ir(&mut self, imm: u8, dst: Register) { + match imm { + 0 => {} + 1 => self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_Ev1, dst, GroupOpcodeID::GROUP2_OP_SHL), + _ => { + self.one_op64_reg_grp(OneByteOpcodeID::OP_GROUP2_EvIb, dst, GroupOpcodeID::GROUP2_OP_SHL); + self.backend.push(imm); + } + } } } + + pub const ABI_ARGS : [Register; 6] = [ + Register::Rdi, + Register::Rsi, + Register::Rdx, + Register::Rcx, + Register::R8, + Register::R9, + ]; + pub const ABI_RET : [Register; 2] = [ + Register::Rax, + Register::Rdx, + ]; } use self::asm::Assembler; @@ -999,69 +1213,188 @@ struct Compiler { free_regs: Vec, /// List of static variables stored in a raw pointer. statics: *const (), + /// List of static variables stored in a raw pointer. + stack_size: usize, } impl Compiler { - fn new(statics: *const ()) -> Compiler { + fn new(stack_size: usize, statics: *const ()) -> Compiler { use compile::asm::Register::*; let asm = x64::Assembler::new(); let start = asm.offset(); Compiler { asm: Assembler { backend: asm }, - start: start, + start, bb_labels: vec![], reg_map: HashMap::new(), // Rbp and Rsp are reserved as a frame pointer and the stack // pointer. free_regs: vec![Rax, Rcx, Rdx, Rbx, Rsi, Rdi, R8 , R9 , R10, R11, R12, R13, R14, R15], - statics: statics, + statics, + stack_size, } } - fn compile(mut self, bytes: &[u8]) -> Result { + fn compile(bytes: &[u8], statics: *const ()) -> Result { let cu : lir::CompilationUnit = bincode::deserialize(bytes)?; + let compiler = Self::new(cu.stack_size, statics); + compiler.compile_cu(&cu) + } + + fn compile_cu(mut self, cu : &lir::CompilationUnit) -> Result { // For each block, create a new dynamic label which identify the // entry of each block. for _ in 0..cu.blocks.len() { + // Create a label for the block address. let label = self.asm.backend.new_dynamic_label(); self.bb_labels.push(label); } - for block in cu.blocks.iter() { - self.compile_block(block)? + if cu.args_defs.len() >= self::asm::ABI_ARGS.len() { + return Err(Error::NYI("Reading arguments from the stack.")); + }; + + // Consume all the registers which are used for arguments. + for (&(r, sz), reg) in cu.args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { + let reg = *reg; + if sz <= 8 { + self.register(r, vec![AllocInfo{ reg, off: 0, sz }]) + } else { + self.register(r, vec![AllocInfo{ reg, off: 0, sz: 8 }]) + } + self.take(reg); + } + + /* + // Generate code for loading arguments values into registers, if + // they are larger than a single register, then they are assumed to + // be pointers to the value. + for (&(r, sz), ar) in cu.args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { + if sz > 8 { + let ar = *ar; + let mut off = 0; + let mut alloc = vec![]; + while off < sz { + match sz - off { + 1 => { + let reg = self.allocate(1)?; + self.asm.movb_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 1, off }); + off += 1; + } + 2 | 3 => { + let reg = self.allocate(2)?; + self.asm.movw_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 2, off }); + off += 2; + } + 4 | 5 | 6 | 7 => { + let reg = self.allocate(4)?; + self.asm.movl_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 4, off }); + off += 4; + } + _ => { + let reg = self.allocate(4)?; + self.asm.movq_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 8, off }); + off += 8; + } + } + } + self.unregister(r); + self.register(r, alloc); + } + } + */ + + // Compile each basic block. + for (id, block) in cu.blocks.iter().enumerate() { + println!("Compile Block {}:", id); + self.compile_block(id, block)? } self.finalize() } - fn compile_block(&mut self, block: &lir::BasicBlockData) -> Result<(), Error> { + fn compile_block(&mut self, id: usize, block: &lir::BasicBlockData) -> Result<(), Error> { + let label = self.bb_labels[id]; + self.asm.backend.dynamic_label(label); + + if id > 0 { + // Note, normally we should support proper inputs for merge blocks, + // but at the moment there is simply nothing else than the returned + // values of a call. + let inputs = vec![Register::Rbp, Register::Rax, Register::Rdx].into_iter(); + + // skip(1) is used to avoid re-binding the frame pointer constantly. + let mut inputs = inputs.skip(1); + + for &(lir, size) in block.input_regs.iter().skip(1) { + let mut off = 0; + let mut allocs = vec![]; + while off < size { + let sz = match size - off { + 1 => 1, + 2 | 3 => 2, + 4 | 5 | 6 | 7 => 4, + 8 | _ => 8, + }; + let reg = inputs.next().unwrap(); + self.take(reg); + allocs.push(AllocInfo{ reg, off, sz }); + off += sz; + } + self.register(lir, allocs); + } + } + for inst in block.insts.iter() { + println!(" Compile Inst: {:?}", inst); self.compile_inst(inst)? } self.compile_terminator(&block.end)?; + println!(" Free Registers Available: {:?}", self.free_regs.len()); Ok(()) } - fn allocate(&mut self, sz: usize) -> Result { + fn allocate(&mut self, _sz: usize) -> Result { match self.free_regs.pop() { - Some(reg) => Ok(reg), + Some(reg) => { + println!(" Allocate: {:?}", reg); + Ok(reg) + } None => Err(Error::NotEnoughRegisters) } } + fn free(&mut self, reg: Register) -> () { + assert!(!self.free_regs.contains(®)); + self.free_regs.push(reg); + } + + fn take(&mut self, reg: Register) { + if let Some(i) = self.free_regs.iter().position(|r| *r == reg) { + self.free_regs.swap_remove(i); + } else { + panic!("Register {:?} is not available.", reg) + } + } fn register(&mut self, r: lir::Reg, alloc: Allocation) { + println!(" Register: {:?} --> {:?}", r, alloc); self.reg_map.insert(r, alloc); } - fn free(&mut self, r: lir::Reg) { + fn unregister(&mut self, r: lir::Reg) { match self.reg_map.remove(&r) { Some(l) => { - for allocInfo in l { - self.free_regs.push(allocInfo.reg); + println!(" Unregister: {:?} --> {:?}", r, l); + for alloc_info in l { + self.free(alloc_info.reg); } } None => (), @@ -1071,6 +1404,7 @@ impl Compiler { fn reuse(&mut self, from: lir::Reg, to: lir::Reg) -> Result<(), Error> { match self.reg_map.remove(&from) { Some(alloc) => { + println!(" Reuse: ({:?}, {:?}) --> {:?}", from, to, alloc); self.register(to, alloc); Ok(()) } @@ -1081,6 +1415,8 @@ impl Compiler { fn reuse_append(&mut self, from: lir::Reg, to: lir::Reg, mut rest: Allocation) -> Result<(), Error> { match self.reg_map.remove(&from) { Some(mut alloc) => { + println!(" Reuse: ({:?}, {:?}) --> {:?}", from, to, alloc); + println!(" Append: {:?} --> {:?}", to, rest); alloc.append(&mut rest); self.register(to, alloc); Ok(()) @@ -1089,10 +1425,100 @@ impl Compiler { } } + fn find_allocation_by_reg(&self, chk: Register) -> Option { + for (lir, alloc) in &self.reg_map { + for &AllocInfo{ reg, .. } in alloc { + if reg == chk { + return Some(*lir) + } + } + } + + None + } + + // This is certainly the least efficient way to do so ... + fn move_group(&mut self, what: &[lir::Reg], into: &[Register]) -> Result<(), Error> { + let mut into_reg = into.iter(); + for lir in what.iter() { + println!(" Move {:?}: {:?}", lir, self.reg_map[lir]); + for alloc_id in 0..self.reg_map[lir].len() { + let dst = match into_reg.next() { + None => return Err(Error::NotEnoughRegistersForMovGrp), + Some(dst) => *dst + }; + let AllocInfo{ reg, sz, .. } = self.reg_map[lir][alloc_id].clone(); + let lir = *lir; + + if reg == dst { + continue + } else { + if !self.free_regs.contains(&dst) { + // The Register is already allocated. + // 1. Locate which allocation this is. + // 2. Move the data into a free register. + match self.find_allocation_by_reg(dst) { + None => { + println!(" Cannot find {:?}", dst); + return Err(Error::AllocatedButNotRegistered) + } + Some(collide) => { + let repl = match self.free_regs.last() { + None => return Err(Error::NotEnoughRegistersForMovGrp), + Some(r) => *r, + }; + let into : Vec<_> = + self.reg_map[&collide].iter().map(|x| { + let &AllocInfo{ reg, .. } = x; + if reg == dst { repl } + else { reg } + }).collect(); + let what = [collide]; + self.move_group(&what, &into)?; + } + } + assert!(self.free_regs.contains(&dst)); + } + + // Copy the content in the destination register. + println!(" {:?}.{:?}: {:?} -> {:?}", lir, alloc_id, reg, dst); + self.take(dst); + match sz { + 0 => {} + 1 => self.asm.movb_rr(reg, dst), + 2 => self.asm.movw_rr(reg, dst), + 4 => self.asm.movl_rr(reg, dst), + 8 => self.asm.movq_rr(reg, dst), + _ => return Err(Error::NYI("MoveGroup: move larger than 8")), + } + + // unregister(lir) + let mut allocs = self.reg_map.remove(&lir).unwrap(); + self.free(allocs[alloc_id].reg); + allocs[alloc_id].reg = dst; + // register(lir, alloc) + self.reg_map.insert(lir, allocs); + } + } + println!(" Moved {:?}: {:?}", lir, self.reg_map[lir]); + } + + Ok(()) + } + + unsafe fn get_static(&self, off: isize) -> &'static T { + let all : *const u8 = mem::transmute(self.statics); + let val_ptr = all.offset(off); + let val_ptr : *const T = mem::transmute(val_ptr); + val_ptr.as_ref::<'static>().unwrap() + } + fn compile_inst(&mut self, inst: &lir::Inst) -> Result<(), Error> { use lir::Inst::*; match inst { - &SetFramePtr(fp, sz, stack_size) => { + &SetFramePtr(fp, _sz, stack_size) => { + let stack_size = stack_size + (16 - stack_size % 16); + // TODO: Save non-volatile registres. dynasm!(self.asm.backend ; push rbp ; mov rbp, rsp @@ -1101,24 +1527,94 @@ impl Compiler { self.register(fp, vec![AllocInfo{ reg: Register::Rbp, off: 0, sz: 8 }]); }, &Static(out, static_offset, static_size) => { - // TODO: Handle various sizes, and save the loaded content - // somewhere. - let reg = self.allocate(static_size)?; - self.register(out, vec![AllocInfo{ reg, off: 0, sz: static_size }]); + // Load the content from the static pointer, and convert it + // into immediate constants baked in the code of the + // generated program. + let static_size = static_size as isize; + let mut off = 0; + let mut alloc = vec![]; + while off < static_size { + match static_size - off { + 1 => { + let reg = self.allocate(1)?; + let value = unsafe { *self.get_static::(static_offset + off) }; + self.asm.movb_ir(value , reg); + alloc.push(AllocInfo{ reg, sz: 1, off: off as usize }); + off += 1; + } + 2 | 3 => { + let reg = self.allocate(2)?; + let value = unsafe { *self.get_static::(static_offset + off) }; + self.asm.movw_ir(value, reg); + alloc.push(AllocInfo{ reg, sz: 2, off: off as usize }); + off += 2; + } + 4 | 5 | 6 | 7 => { + let reg = self.allocate(4)?; + let value = unsafe { *self.get_static::(static_offset + off) }; + self.asm.movl_ir(value, reg); + alloc.push(AllocInfo{ reg, sz: 4, off: off as usize }); + off += 4; + } + _ => { + let reg = self.allocate(8)?; + let value = unsafe { *self.get_static::(static_offset + off) }; + self.asm.movq_ir(value, reg); + alloc.push(AllocInfo{ reg, sz: 8, off: off as usize }); + off += 8; + } + } + } + self.register(out, alloc); }, &CopyImm(out, value, sz) => { - let reg = self.allocate(sz)?; - self.register(out, vec![AllocInfo{ reg, sz, off: 0 }]); // TODO: move the value to the given registers. - match sz { - _ => return Err(Error::NYI), + let mut off = 0; + let mut alloc = vec![]; + while off < sz { + match sz - off { + 1 => { + let reg = self.allocate(1)?; + self.asm.movb_ir(value as i8 , reg); + alloc.push(AllocInfo{ reg, sz: 1, off }); + off += 1; + } + 2 | 3 => { + let reg = self.allocate(2)?; + self.asm.movw_ir(value as i16, reg); + alloc.push(AllocInfo{ reg, sz: 2, off }); + off += 2; + } + 4 | 5 | 6 | 7 => { + let reg = self.allocate(4)?; + self.asm.movl_ir(value as i32, reg); + alloc.push(AllocInfo{ reg, sz: 4, off }); + off += 4; + } + _ => { + let reg = self.allocate(8)?; + self.asm.movq_ir(value as i64, reg); + alloc.push(AllocInfo{ reg, sz: 8, off }); + off += 8; + } + } } + self.register(out, alloc); } &Resize(out, input, sz) => { - let input = self.reg_map[&input].clone(); + let input_alloc = self.reg_map[&input].clone(); + assert_eq!(input_alloc.len(), 1); + let AllocInfo{ reg: ir, off: io, sz: iz } = input_alloc[0]; let reg = self.allocate(sz)?; + + // Copy the value. + self.asm.movq_rr(ir, reg); + // Shift it, such that the highest bit becomes the sign bit. + self.asm.shlq_ir((64 - (iz + io) * 8) as u8, reg); + // Shift it back, to sign-extend the sign bit. + self.asm.sarq_ir((64 - iz * 8) as u8, reg); + self.register(out, vec![AllocInfo{ reg, sz, off: 0 }]); - // TODO: sign-extend? }, &Add(out, lhs, rhs) => { let lhs_alloc = self.reg_map[&lhs].clone(); @@ -1127,104 +1623,378 @@ impl Compiler { assert_eq!(rhs_alloc.len(), 1); let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); // Dirty: Assumes that operands are always consumed, except // for the frame pointer. - if lr != Register::Rbp { - self.asm.addq_rr(lr, rr); - self.reuse(lhs, out); + let (lr, rr, reg) = if lr == Register::Rbp { + (lr, rr, rhs) } else { - self.asm.addq_rr(rr, lr); - self.reuse(rhs, out); + (rr, lr, lhs) + }; + match ls { + 1 => self.asm.addb_rr(lr, rr), + 2 => self.asm.addw_rr(lr, rr), + 4 => self.asm.addl_rr(lr, rr), + 8 => self.asm.addq_rr(lr, rr), + _ => return Err(Error::NYI("Unexpected Add size")), } + self.reuse(reg, out)?; }, - &Sub(out, lhs, rhs) => { return Err(Error::NYI) }, - &Mul(out, lhs, rhs) => { + &Sub(out, lhs, rhs) => { let lhs_alloc = self.reg_map[&lhs].clone(); let rhs_alloc = self.reg_map[&rhs].clone(); assert_eq!(lhs_alloc.len(), 1); assert_eq!(rhs_alloc.len(), 1); let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + // Dirty: Assumes that operands are always consumed, except + // for the frame pointer. + let (lr, rr, reg) = if lr == Register::Rbp { + (lr, rr, rhs) + } else { + (rr, lr, lhs) + }; + match ls { + 1 => self.asm.subb_rr(lr, rr), + 2 => self.asm.subw_rr(lr, rr), + 4 => self.asm.subl_rr(lr, rr), + 8 => self.asm.subq_rr(lr, rr), + _ => return Err(Error::NYI("Unexpected Add size")), + } + self.reuse(reg, out)?; + }, + &Mul(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, .. } = rhs_alloc[0]; // Dirty: Assumes that operands are always consumed, except // for the frame pointer. if lr != Register::Rbp { self.asm.imulq_rr(lr, rr); - self.reuse(lhs, out); + self.reuse(rhs, out)?; } else { self.asm.imulq_rr(rr, lr); - self.reuse(rhs, out); + self.reuse(lhs, out)?; } }, - &Div(out, lhs, rhs) => { return Err(Error::NYI) }, - &Rem(out, lhs, rhs) => { return Err(Error::NYI) }, - &BitXor(out, lhs, rhs) => { return Err(Error::NYI) }, - &BitAnd(out, lhs, rhs) => { return Err(Error::NYI) }, - &BitOr(out, lhs, rhs) => { return Err(Error::NYI) }, - &Shl(out, lhs, rhs) => { return Err(Error::NYI) }, - &Shr(out, lhs, rhs) => { return Err(Error::NYI) }, + &Div(_out, _lhs, _rhs) => { return Err(Error::NYI("Div")) }, + &Rem(_out, _lhs, _rhs) => { return Err(Error::NYI("Rem")) }, + &BitXor(_out, _lhs, _rhs) => { return Err(Error::NYI("BitXor")) }, + &BitAnd(_out, _lhs, _rhs) => { return Err(Error::NYI("BitAnd")) }, + &BitOr(_out, _lhs, _rhs) => { return Err(Error::NYI("BitOr")) }, + &Shl(_out, _lhs, _rhs) => { return Err(Error::NYI("Shl")) }, + &Shr(_out, _lhs, _rhs) => { return Err(Error::NYI("Shr")) }, &Eq(out, lhs, rhs) => { let lhs_alloc = self.reg_map[&lhs].clone(); let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); let reg = self.allocate(1)?; - // TODO: handle size 1 separately, as the content might not - // be signed-extended, such as with the setcc instruction. - self.asm.cmpq_rr(lhs_alloc[0].reg, rhs_alloc[0].reg); + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Eq larger than 8")), + } self.asm.setcc_r(Condition::E, reg); self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); }, - &Lt(out, lhs, rhs) => { return Err(Error::NYI) }, - &Le(out, lhs, rhs) => { return Err(Error::NYI) }, - &Ne(out, lhs, rhs) => { return Err(Error::NYI) }, - &Gt(out, lhs, rhs) => { return Err(Error::NYI) }, - &Ge(out, lhs, rhs) => { return Err(Error::NYI) }, + &Lt(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + let reg = self.allocate(1)?; + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Lt larger than 8")), + } + self.asm.setcc_r(Condition::L, reg); + self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); + }, + &Le(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + let reg = self.allocate(1)?; + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Le larger than 8")), + } + self.asm.setcc_r(Condition::LE, reg); + self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); + }, + &Ne(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + let reg = self.allocate(1)?; + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Ne larger than 8")), + } + self.asm.setcc_r(Condition::NE, reg); + self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); + }, + &Gt(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + let reg = self.allocate(1)?; + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Gt larger than 8")), + } + self.asm.setcc_r(Condition::G, reg); + self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); + }, + &Ge(out, lhs, rhs) => { + let lhs_alloc = self.reg_map[&lhs].clone(); + let rhs_alloc = self.reg_map[&rhs].clone(); + assert_eq!(lhs_alloc.len(), 1); + assert_eq!(rhs_alloc.len(), 1); + let AllocInfo{ reg: lr, sz: ls, .. } = lhs_alloc[0]; + let AllocInfo{ reg: rr, sz: rs, .. } = rhs_alloc[0]; + assert_eq!(ls, rs); + let reg = self.allocate(1)?; + match ls { + 1 => self.asm.cmpb_rr(lr, rr), + 2 => self.asm.cmpw_rr(lr, rr), + 4 => self.asm.cmpl_rr(lr, rr), + 8 => self.asm.cmpq_rr(lr, rr), + _ => return Err(Error::NYI("Ge larger than 8")), + } + self.asm.setcc_r(Condition::GE, reg); + self.register(out, vec![AllocInfo{ reg, off:0, sz: 1 }]); + }, - &Chk(outAndFlags, out) => { + &Chk(out_flags, out) => { // Warning: This instructions assumes that no other // intruction got added in-between. let out_alloc = self.reg_map[&out].clone(); assert_eq!(out_alloc.len(), 1); let reg = self.allocate(1)?; self.asm.setcc_r(Condition::O, reg); - self.reuse_append(out, outAndFlags, vec![AllocInfo{ reg, sz: 1, off: out_alloc[0].sz }]); + self.reuse_append(out, out_flags, vec![AllocInfo{ reg, sz: 1, off: out_alloc[0].sz }])?; }, - &Store(addr, value, sz) => { + &Store(addr, value, _sz) => { let addr_alloc = self.reg_map[&addr].clone(); let value_alloc = self.reg_map[&value].clone(); assert_eq!(addr_alloc.len(), 1); - assert_eq!(value_alloc.len(), 1); let AllocInfo{ reg: ar, .. } = addr_alloc[0]; - let AllocInfo{ reg: vr, .. } = value_alloc[0]; - match sz { - 1 => self.asm.movb_rm(vr, ar, 0), - 2 => self.asm.movw_rm(vr, ar, 0), - 4 => self.asm.movl_rm(vr, ar, 0), - 8 => self.asm.movq_rm(vr, ar, 0), - _ => return Err(Error::NYI), + for &AllocInfo{ reg: vr, off: offset, sz: size } in value_alloc.iter() { + match size { + 0 => {} + 1 => self.asm.movb_rm(vr, ar, offset as i32), + 2 => self.asm.movw_rm(vr, ar, offset as i32), + 4 => self.asm.movl_rm(vr, ar, offset as i32), + 8 => self.asm.movq_rm(vr, ar, offset as i32), + _ => return Err(Error::NYI("Store larger than 8")), + } } }, &Load(out, addr, sz) => { let addr_alloc = self.reg_map[&addr].clone(); assert_eq!(addr_alloc.len(), 1); let AllocInfo{ reg: ar, .. } = addr_alloc[0]; - let reg = self.allocate(sz)?; - match sz { - 1 => self.asm.movb_mr(ar, 0, reg), - 2 => self.asm.movw_mr(ar, 0, reg), - 4 => self.asm.movl_mr(ar, 0, reg), - 8 => self.asm.movq_mr(ar, 0, reg), - _ => return Err(Error::NYI), + let mut off = 0; + let mut alloc = vec![]; + while off < sz { + match sz - off { + 0 => {} + 1 => { + let reg = self.allocate(1)?; + self.asm.movb_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 1, off }); + off += 1; + } + 2 | 3 => { + let reg = self.allocate(2)?; + self.asm.movw_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 2, off }); + off += 2; + } + 4 | 5 | 6 | 7 => { + let reg = self.allocate(4)?; + self.asm.movl_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 4, off }); + off += 4; + } + _ => { + let reg = self.allocate(8)?; + self.asm.movq_mr(ar, off as i32, reg); + alloc.push(AllocInfo{ reg, sz: 8, off }); + off += 8; + } + } } - self.register(out, vec![AllocInfo{ reg, sz, off: 0 }]); + self.register(out, alloc); }, - &StoreInto(data_reg, value, offset, sz) => { return Err(Error::NYI) }, - &LoadFrom(out, data_reg, offset, sz) => { return Err(Error::NYI) }, + &StoreInto(into, value, offset, sz) => { + if sz == 0 { + return Ok(()) + } + let val_alloc = self.reg_map[&value].clone(); + let into_alloc = self.reg_map[&into].clone(); + let mut val_iter = val_alloc.iter(); + let mut into_iter = into_alloc.iter(); + let mut val_last = val_iter.next(); + let mut into_last = into_iter.next(); + loop { + match (val_last, into_last) { + (None, _) => break, + (_, None) => return Err(Error::StoreIntoMissTarget(0)), + (Some(&AllocInfo{ reg: vr, off: vo, sz: vs }), + Some(&AllocInfo{ reg: ir, off: io, sz: is })) => { + assert!(vo + vs <= sz); + let vo = vo + offset; + if io == vo && is == vs { + // The stored value matches exactly the + // component of the destination. + match is { + 0 => {} + 1 => self.asm.movb_rr(vr, ir), + 2 => self.asm.movw_rr(vr, ir), + 4 => self.asm.movl_rr(vr, ir), + 8 => self.asm.movq_rr(vr, ir), + _ => return Err(Error::NYI("StoreInto: allocation larger than 8")), + } + + val_last = val_iter.next(); + into_last = into_iter.next(); + } else if io + is <= vo { + into_last = into_iter.next(); + } else if vo + vs <= io { + return Err(Error::StoreIntoMissTarget(1)); + } else if io <= vo && vo + vs <= io + is { + // The value is a subset of the + // destination. Use masks to filter the low + // and high part of the destinations, and + // copy the value into it. + + // Allocate a temporary. + let tmp_vr = self.allocate(8)?; + + // Compute the masks used to filter out the + // target value. + let low_sz = vo - io; + let data_off = low_sz; + let data_sz = vs; + let high_off = low_sz + data_sz; + assert!(high_off <= is); + let high_sz = is - high_off; + + let mut mask : u64 = 0; + if high_sz > 0 { + let mut high_mask : u64 = 0xffff_ffff_ffff_ffff; + high_mask = high_mask >> (64 - high_sz * 8); + high_mask = high_mask << (high_off * 8); + mask = high_mask; + } + + if low_sz > 0 { + let mut low_mask : u64 = 0xffff_ffff_ffff_ffff; + low_mask = low_mask >> (64 - low_sz * 8); + mask = mask | low_mask; + } + + // Filter the low & high part. + self.asm.movq_ir(mask as i64, tmp_vr); + self.asm.andq_rr(tmp_vr, ir); + + // Copy the data. + let mask : u64 = 0xffff_ffff_ffff_ffff_u64 >> (64 - data_sz * 8); + self.asm.movq_ir(mask as i64, tmp_vr); + self.asm.andq_rr(vr, tmp_vr); + self.asm.shrq_ir((data_off * 8) as u8, tmp_vr); + self.asm.orq_rr(tmp_vr, ir); + + self.free(tmp_vr); + + val_last = val_iter.next(); + } else if io < vo + sz && vo < io + is { + // The stored value span multiple allocations. + return Err(Error::NYI("StoreInto: miss-aligned content")) + } else { + // The stored value span multiple allocations. + return Err(Error::NYI("StoreInto: ???")) + } + } + } + } + }, + &LoadFrom(out, from, offset, sz) => { + let from_alloc = self.reg_map[&from].clone(); + let mut dst_off = 0; + let mut alloc = vec![]; + for &AllocInfo{ reg: vr, off, sz: size } in from_alloc.iter() { + if off == offset && size == sz { + // The loaded value matches exactly the content + // which is being loaded. + let reg = self.allocate(size)?; + match size { + 0 => {} + 1 => self.asm.movb_rr(vr, reg), + 2 => self.asm.movw_rr(vr, reg), + 4 => self.asm.movl_rr(vr, reg), + 8 => self.asm.movq_rr(vr, reg), + _ => return Err(Error::NYI("LoadFrom: allocation larger than 8")), + } + + alloc.push(AllocInfo{ reg, sz: size, off: dst_off }); + dst_off += size; + } else if off <= offset && offset + sz <= off + size { + return Err(Error::NYI("LoadFrom: subset content")) + } else if off < offset + sz && offset < off + size { + // The loaded value is spread across multiple + // allocations. + return Err(Error::NYI("LoadFrom: miss-aligned content")) + } + } + self.register(out, alloc); + }, &Live(_) => {}, &Dead(reg) => { // Note, the register might have already been freed by one // of call to reuse or reuse_append functions. - self.free(reg); + self.unregister(reg); }, }; @@ -1233,15 +2003,144 @@ impl Compiler { fn compile_terminator(&mut self, term: &lir::Terminator) -> Result<(), Error> { use lir::Terminator::*; + println!(" Compile Terminator {:?}:", term); match term { - &Return { value } => {} - &Unwind => {} - &Unreachable => {} - &Goto { target } => {} - &SwitchInt { value, range, ref targets, otherwise } => {} - &Call { function, ref args, return_target, unwind_target } => {} + &Return { value } => { + if let Some(value) = value { + let regs = [value]; + self.move_group(®s, &self::asm::ABI_RET)?; + self.unregister(value); + } + + // TODO: Pop non-volatile registers. + let stack_size = self.stack_size; + let stack_size = stack_size + (16 - stack_size % 16); + dynasm!(self.asm.backend + ; add rsp, stack_size as _ + ; pop rbp + ; ret + ); + Ok(()) + } + &Unwind => { + // TODO: We should be calling the _Unwind_Resume@PLT + // function, but at the moment I have not looked from where + // we can get it from. + dynasm!(self.asm.backend + ; int3 + ); + Ok(()) + } + &Unreachable => { + // Not part of Rust, but whatever ... + dynasm!(self.asm.backend + ; int3 + ); + Ok(()) + } + &Goto { target } => { + let label = self.bb_labels[target]; + dynasm!(self.asm.backend + ; jmp => label + ); + Ok(()) + } + &SwitchInt { value, /*range,*/ ref targets, otherwise, .. } => { + // TODO: Optimize for boolean values. + let val_alloc = self.reg_map[&value].clone(); + let AllocInfo{ reg: vr, sz: vz, .. } = val_alloc[0]; + assert_eq!(val_alloc.len(), 1); + let ir = self.allocate(1)?; + + for &(val, dest) in targets.iter() { + // Load the value in the given register. + match vz { + 1 => { + self.asm.movb_ir(val as i8 , ir); + self.asm.cmpb_rr(vr, ir); + } + 2 => { + self.asm.movw_ir(val as i16, ir); + self.asm.cmpw_rr(vr, ir); + } + 4 => { + self.asm.movl_ir(val as i32, ir); + self.asm.cmpl_rr(vr, ir); + } + 8 => { + self.asm.movq_ir(val as i64, ir); + self.asm.cmpq_rr(vr, ir); + } + _ => return Err(Error::NYI("SwitchInt comparison is larger than 8")), + } + // Compare the value + // Jump to the destination if equal. + let label = self.bb_labels[dest]; + dynasm!(self.asm.backend + ; je => label + ); + } + + if let Some(other) = otherwise { + // Unconditionally jump to the otherwise place if none + // of the value matched. + let label = self.bb_labels[other]; + dynasm!(self.asm.backend + ; jmp => label + ); + } + + // Trap if none of the branches are taken. + dynasm!(self.asm.backend + ; int3 + ); + + self.free(ir); + self.unregister(value); + Ok(()) + } + &Call { function, ref args, return_target, unwind_target } => { + if args.len() >= self::asm::ABI_ARGS.len() { + return Err(Error::NYI("Writting arguments to the stack.")); + }; + + // bind the arguments to specific registers. + self.move_group(&args, &self::asm::ABI_ARGS)?; + + // Make the call. + let fun_alloc = self.reg_map[&function].clone(); + assert_eq!(fun_alloc.len(), 1); + let AllocInfo{ reg: fr, sz: fz, .. } = fun_alloc[0]; + assert_eq!(fz, 8); + self.asm.call_r(fr); + + // Free the function pointer. + self.unregister(function); + + // Free the argument registers. + for arg in args.iter() { + self.unregister(*arg) + } + + if let Some((_, ret_id)) = return_target { + // Note: The return value register is being set by the + // compile function. + let label = self.bb_labels[ret_id]; + dynasm!(self.asm.backend + ; jmp => label + ); + } + if let Some(uwd_id) = unwind_target { + let label = self.bb_labels[uwd_id]; + dynasm!(self.asm.backend + ; jmp => label + ); + } + + // TODO: Handle unwinding. + Ok(()) + } } - Ok(()) } fn finalize(self) -> Result { diff --git a/lib/src/context.rs b/lib/src/context.rs index 1f4cb3d..105b0b3 100644 --- a/lib/src/context.rs +++ b/lib/src/context.rs @@ -3,24 +3,35 @@ /// as well as the heuristics for enterring the JIT. use compile::JitCode; +use std::rc::Rc; /// Opaque structure which is used to store the function mapping, and tune /// the JIT parameters. pub struct JitContext { - code: Option + code: Option> } impl JitContext { - pub fn lookup<'ctx, Args, Output>(&self, bytes: &[u8], defs: *const ()) -> Option<&'ctx Fn> { + pub fn lookup(&self, bytes: &[u8], defs: *const ()) -> Option> { match &self.code { - &Some(ref jit) => Some(jit.get_fn::<'ctx, Args, Output>()), - &None => match JitCode::compile(bytes, defs) { - Ok(jit) => { - // TODO: - // self.code = Some(jit); - Some(jit.get_fn::<'ctx, Args, Output>()) + &Some(ref jit) => { + println!("Found JIT Code in the context"); + Some(jit.clone()) + } + &None => { + println!("Did not found JIT Code in the context.\nStart compiling ..."); + match JitCode::compile(bytes, defs) { + Ok(jit) => { + let jit = Rc::new(jit); + // TODO: Store the Jit code on the context. + // self.code = Some(jit); + Some(jit) + } + Err(e) => { + println!("JIT Compiler Error: {:?}", e); + None + } } - Err(_) => None } } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 0647af6..1fb10eb 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -11,6 +11,7 @@ #![plugin(dynasm)] pub use std::marker::PhantomData; +use std::rc::Rc; // dynasm is "currently" used by the compiler as a way to generate code // without the burden of implementing yet another macro-assembler. @@ -30,6 +31,7 @@ mod context; mod compile; pub use context::JitContext; +pub use compile::JitCode; /// This trait should be implemented by every function that we want to be able to Jit. This trait /// implements the Fn trait to make this function callable, and to make it a potential entry point @@ -43,21 +45,50 @@ pub use context::JitContext; /// CurryN types are made to wrap the native function pointer and provide /// a FnOnce, FnMut, and Fn traits implementation. -pub struct Curry0 { pub fun: fn() -> Output } +pub enum Curry0 { + Native(fn() -> Output), + // Use an Rc in order to ensure that the JitCode is kept alive as long + // as the function is running on the stack. + Jit(Rc) +} impl FnOnce<()> for Curry0 { type Output = Output; extern "rust-call" fn call_once(self, _: ()) -> Output { - (self.fun)() + match self { + Curry0::Native(fun) => fun(), + Curry0::Jit(jit) => { + let fun : fn() -> Output = unsafe { + std::mem::transmute(jit.get_fn()) + }; + fun() + } + } } } impl FnMut<()> for Curry0 { extern "rust-call" fn call_mut(&mut self, _: ()) -> Output { - (self.fun)() + match self { + &mut Curry0::Native(ref mut fun) => fun(), + &mut Curry0::Jit(ref mut jit) => { + let fun : fn() -> Output = unsafe { + std::mem::transmute(jit.get_fn()) + }; + fun() + } + } } } impl Fn<()> for Curry0 { extern "rust-call" fn call(&self, _: ()) -> Output { - (self.fun)() + match self { + &Curry0::Native(ref fun) => fun(), + &Curry0::Jit(ref jit) => { + let fun : fn() -> Output = unsafe { + std::mem::transmute(jit.get_fn()) + }; + fun() + } + } } } @@ -77,21 +108,48 @@ macro_rules! fn_ty { macro_rules! curry_decl { ($name:ident<($($arg:ident),*) -> $ret:ident>) => { - pub struct $name<$($arg,)* $ret> { pub fun: fn($($arg),*) -> $ret } + pub enum $name<$($arg,)* $ret> { + Native(fn($($arg),*) -> $ret), + Jit(Rc) + } impl<$($arg,)* $ret> FnOnce for $name<$($arg,)* $ret> { type Output = $ret; extern "rust-call" fn call_once(self, args: fn_ty!{$($arg),*}) -> $ret { - curry_call!{ (self.fun) => args: ($($arg),*) } + match self { + $name::Native(fun) => curry_call!{ fun => args: ($($arg),*) }, + $name::Jit(jit) => { + let fun : fn($($arg),*) -> $ret = unsafe { + std::mem::transmute(jit.get_fn()) + }; + curry_call!{ fun => args: ($($arg),*) } + } + } } } impl<$($arg,)* $ret> FnMut for $name<$($arg,)* $ret> { extern "rust-call" fn call_mut(&mut self, args: fn_ty!{$($arg),*}) -> $ret { - curry_call!{ (self.fun) => args: ($($arg),*) } + match self { + &mut $name::Native(ref mut fun) => curry_call!{ fun => args: ($($arg),*) }, + &mut $name::Jit(ref mut jit) => { + let fun : fn($($arg),*) -> $ret = unsafe { + std::mem::transmute(jit.get_fn()) + }; + curry_call!{ fun => args: ($($arg),*) } + } + } } } impl<$($arg,)* $ret> Fn for $name<$($arg,)* $ret> { extern "rust-call" fn call(&self, args: fn_ty!{$($arg),*}) -> $ret { - curry_call!{ (self.fun) => args: ($($arg),*) } + match self { + &$name::Native(ref fun) => curry_call!{ fun => args: ($($arg),*) }, + &$name::Jit(ref jit) => { + let fun : fn($($arg),*) -> $ret = unsafe { + std::mem::transmute(jit.get_fn()) + }; + curry_call!{ fun => args: ($($arg),*) } + } + } } } } @@ -107,8 +165,11 @@ curry_decl!{ Curry4<(A0,A1,A2,A3) -> Output> } /// the number of hit-counts, this function will select either the function /// statically compiled by rustc, or the result of the compilation performed /// by HolyJit. -pub trait HolyJitFn { - fn get_fn(&self) -> &Fn; +pub trait HolyJitFn { + type Curry; + type Output; + fn get_fn(&self) -> Self::Curry; + fn from_code(jit: Rc) -> Self::Curry where Self : Sized; fn get_jc<'a>(&self, args: &'a Args) -> &'a JitContext; } @@ -117,7 +178,9 @@ pub trait HolyJitFn { /// arguments type, and the second parameter defines Output of the function. /// The last parameter T corresponds to the structure which is wrapping the /// function pointer. This structure should implement the HolyJitFn trait. -pub struct HolyJitFnWrapper>{ +pub struct HolyJitFnWrapper + where T : HolyJitFn +{ /// Reference to the statically compiled function. pub fun: T, @@ -136,24 +199,29 @@ pub struct HolyJitFnWrapper>{ pub defs: *const (), // Keep track of the types. - pub _proto: PhantomData> + pub _proto: PhantomData> } -impl> HolyJitFnWrapper { +impl HolyJitFnWrapper + where T : HolyJitFn +{ /// Given a list of argument, extract a JitContext from it, and look for /// existing compiled code. Otherwise, either return the static /// function, or the newly JIT compiled function. - fn select_fn<'ctx>(&'ctx self, args: &Args) -> &'ctx Fn { + fn select_fn<'ctx>(&'ctx self, args: &Args) -> Curry { let jc = self.fun.get_jc(args); - let jitcode = jc.lookup::<'ctx, Args, Output>(self.bytes, self.defs); - match jitcode { - Some(code) => code, + match jc.lookup(self.bytes, self.defs) { + Some(code) => T::from_code(code), None => self.fun.get_fn() } } } -impl> FnOnce for HolyJitFnWrapper { +impl FnOnce + for HolyJitFnWrapper + where T : HolyJitFn, + Curry : FnOnce +{ type Output = Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output { let f = { @@ -163,7 +231,11 @@ impl> FnOnce for HolyJitFnWrappe f.call_once(args) } } -impl> FnMut for HolyJitFnWrapper { +impl> FnMut + for HolyJitFnWrapper + where T : HolyJitFn, + Curry : FnMut +{ extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output { let mut f = { let ra = &args; @@ -172,7 +244,11 @@ impl> FnMut for HolyJitFnWrapper f.call_mut(args) } } -impl> Fn for HolyJitFnWrapper { +impl> Fn + for HolyJitFnWrapper + where T : HolyJitFn, + Curry : Fn +{ extern "rust-call" fn call(&self, args: Args) -> Self::Output { let f = { let ra = &args; @@ -190,11 +266,17 @@ macro_rules! curry { (($a0:ty,$a1:ty,$a2:ty) -> $ret_type:ty) => { $crate::Curry3<$a0,$a1,$a2,$ret_type> }; (($a0:ty,$a1:ty,$a2:ty,$a3:ty) -> $ret_type:ty) => { $crate::Curry4<$a0,$a1,$a2,$a3,$ret_type> }; - (() -> $ret_type:ty = $e:ident ) => { $crate::Curry0{ fun: $e } }; - (($a0:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry1{ fun: $e } }; - (($a0:ty,$a1:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry2{ fun: $e } }; - (($a0:ty,$a1:ty,$a2:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry3{ fun: $e } }; - (($a0:ty,$a1:ty,$a2:ty,$a3:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry4{ fun: $e } }; + (() -> $ret_type:ty = $e:ident ) => { $crate::Curry0::Native( $e ) }; + (($a0:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry1::Native( $e ) }; + (($a0:ty,$a1:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry2::Native( $e ) }; + (($a0:ty,$a1:ty,$a2:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry3::Native( $e ) }; + (($a0:ty,$a1:ty,$a2:ty,$a3:ty) -> $ret_type:ty = $e:ident ) => { $crate::Curry4::Native( $e ) }; + + (() -> $ret_type:ty = < $e:ident > ) => { $crate::Curry0::Jit( $e ) }; + (($a0:ty) -> $ret_type:ty = < $e:ident > ) => { $crate::Curry1::Jit( $e ) }; + (($a0:ty,$a1:ty) -> $ret_type:ty = < $e:ident > ) => { $crate::Curry2::Jit( $e ) }; + (($a0:ty,$a1:ty,$a2:ty) -> $ret_type:ty = < $e:ident > ) => { $crate::Curry3::Jit( $e ) }; + (($a0:ty,$a1:ty,$a2:ty,$a3:ty) -> $ret_type:ty = < $e:ident > ) => { $crate::Curry4::Jit( $e ) }; } #[macro_export] @@ -221,7 +303,7 @@ macro_rules! jit { // proper way to generate new identifier, we use this aliasing // technique to avoid conflicting hard-coded names. #[allow(non_upper_case_globals)] - const $fun : $crate::HolyJitFnWrapper = $crate::HolyJitFnWrapper{ + const $fun : $crate::HolyJitFnWrapper $ret_type }, $fun> = $crate::HolyJitFnWrapper{ fun : $fun{ curry: curry!{ ($($typ),*) -> $ret_type = $delegate } }, bytes : &[ 0 /* placeholder for holyjit plugin */ ], defs : &(1u8, 2u8) as *const _ as *const (), @@ -229,10 +311,20 @@ macro_rules! jit { }; #[allow(non_camel_case_types)] struct $fun { curry: curry!{ ($($typ),*) -> $ret_type } } - impl $crate::HolyJitFn for $fun { - fn get_fn(&self) -> &Fn { + impl $crate::HolyJitFn for $fun { + type Curry = curry!{ ($($typ),*) -> $ret_type }; + type Output = $ret_type; + fn get_fn(&self) -> Self::Curry { // Return the statically compiled function. - &self.curry + if let curry!{ ($($typ),*) -> $ret_type = fun } = self.curry { + curry!{ ($($typ),*) -> $ret_type = fun } + } else { + panic!("The impossible happened!") + } + } + fn from_code(jit: std::rc::Rc<$crate::JitCode>) -> Self::Curry { + // Return the dynamically compiled function. + curry!{($($typ),*) -> $ret_type = < jit > } } fn get_jc<'a>(&self, args: &'a fn_ty!{$($typ),*}) -> &'a $crate::JitContext { // Use a match statement to fake the arguments bindings of diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index 04bc745..7ccb21c 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -318,13 +318,12 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { statics.iter() // TODO: We need to create a reference to the statics types. .map(|rv| match rv { - &mir::Rvalue::Ref(ref region, _, mir::Lvalue::Static(ref st)) => { + &mir::Rvalue::Ref(ref region, _, mir::Lvalue::Static(ref st)) => // region == tcx.types.re_erased - self.tcx.mk_imm_ref(region.clone(), st.ty) - } - &mir::Rvalue::Use(mir::Operand::Constant(ref constant)) => { - constant.ty - } + self.tcx.mk_imm_ref(region.clone(), st.ty), + &mir::Rvalue::Use(mir::Operand::Constant(ref constant)) => + constant.ty, + &mir::Rvalue::Cast(_, _, ty) => ty, _ => unreachable!("Unexpected Rvalue: {:?}", rv) }) .collect(); diff --git a/plugin/src/trans.rs b/plugin/src/trans.rs index 73a3df4..0edfcba 100644 --- a/plugin/src/trans.rs +++ b/plugin/src/trans.rs @@ -50,7 +50,7 @@ pub struct Transpiler<'a, 'tcx: 'a> { } struct Local<'tcx> { - idx: mir::Local, + _idx: mir::Local, ty: ty::Ty<'tcx>, off: usize, size: usize, @@ -161,9 +161,11 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { )) } + /* fn mir(&mut self) -> Result<(), Error> { Ok(()) } + */ fn locals(&mut self, mir: &mir::Mir<'tcx>) -> Result<(Vec, usize), Error> { // Loop over arguments and locals to find out about their sizes @@ -176,7 +178,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // see librustc_mir/transform/inline.rs (type_size_of) // let mut stack : usize = 0; - let mut args_size : usize = 0; + let args_size : usize = 0; // TODO: Cumulate the stack size. let mut args_defs : Vec = Vec::with_capacity(mir.arg_count); for (local, decl) in mir.local_decls.iter_enumerated() { let layout = decl.ty.layout(self.tcx, self.param_env)?; @@ -199,13 +201,22 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // Bump the stack pointer with the size of the local. let (off, args_size) = self.add_type(decl.ty, args_size)?; let reg = self.get_new_reg(); - args_defs.push((reg, args_size - off)); + let size = args_size - off; + + // TODO: use proper pointer size. + if size > 8 { + // Arguments are pass by pointer, if larger than + // a pointer size. (unless it is a fat_pointer, + // or is this only for returned values?) + args_defs.push((reg, 8)); + } else { + args_defs.push((reg, size)); + } Local { - idx: local, + _idx: local, ty: decl.ty, - off: off, - size: args_size - off, + off, size, reg: Some(reg), } }, @@ -217,7 +228,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { stack = bump; Local { - idx: local, + _idx: local, ty: decl.ty, off: off, size: bump - off, @@ -252,7 +263,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { }; let lir = self.new_block(); - let id = self.blocks_map.insert(mir, lir); + let _id = self.blocks_map.insert(mir, lir); lir } @@ -304,7 +315,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { // Collect the sequence of instruction to fetch the value. - let InstSeq(mut rv_insts, rv_reg, rv_ty) = + let InstSeq(rv_insts, rv_reg, rv_ty) = self.rvalue(rvalue).or_else( report_nyi!("StatementKind::Assign(_, {:?})", rvalue))?; @@ -330,7 +341,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { Ok(insts) } - mir::StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { + mir::StatementKind::SetDiscriminant{ .. } => { Err(Error::NYI) } mir::StatementKind::InlineAsm { .. } => { @@ -358,8 +369,8 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { fn type_of_local(&self, index: mir::Local) -> ty::Ty<'tcx> { self.locals_map[&index].ty } - fn offset_of_local(&self, index: mir::Local) -> usize { - self.locals_map[&index].off + fn offset_of_local(&self, index: mir::Local) -> isize { + 0 - (self.locals_map[&index].off as isize) - (self.locals_map[&index].size as isize) } fn size_of_local(&self, index: mir::Local) -> usize { self.locals_map[&index].size @@ -367,7 +378,9 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { fn offset_of(&self, ty: ty::Ty<'tcx>, field: &mir::Field) -> Result { let layout = ty.layout(self.tcx, self.param_env)?; - Ok(layout.field_offset(&self.tcx.data_layout, field.index(), None).bytes() as usize) + let off = layout.field_offset(&self.tcx.data_layout, field.index(), None).bytes(); + println!("offset_of({:?}, {:?}) -> offset: {:?} {{ layout: {:?} }}", ty, field, off, layout); + Ok(off as usize) } /// Given a type and the last bump size pointer, this function look at @@ -399,11 +412,12 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } fn register_static(&mut self, rvalue: &mir::Rvalue<'tcx>, ty: ty::Ty<'tcx>) -> Result<(lir::Imm, lir::Sz), Error> { - println!("register_static: {:?} : {:?}", rvalue, ty); self.statics.push(rvalue.clone()); let bump = self.statics_size; let (off, bump) = self.add_type(ty, bump)?; self.statics_size = bump; + let layout = ty.layout(self.tcx, self.param_env)?; + println!("register_static:\n: {:?}\n: {:?}\n: {:?}\n: {:?}", rvalue, ty, bump - off, layout); Ok((off as lir::Imm, bump - off as lir::Sz)) } @@ -411,7 +425,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { { match (lvalue, lvctx) { (&mir::Lvalue::Local(index), LvalueCtx::Address) => { - if let Some(reg) = self.reg_of_local(index) { + if let Some(_reg) = self.reg_of_local(index) { return Err(Error::NYI); } let off = self.get_new_reg(); @@ -434,6 +448,15 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { (&mir::Lvalue::Local(index), LvalueCtx::Value) => { if let Some(reg) = self.reg_of_local(index) { let ty = self.type_of_local(index); + let ptr_size = 8; // TODO: use proper pointer size. + if self.size_of_local(index) > ptr_size { + let val = self.get_new_reg(); + return Ok(InstSeq(vec![ + lir::Inst::Live(val), + lir::Inst::Load(val, reg, self.size_of_local(index) as lir::Sz), + lir::Inst::Dead(reg), + ], val, ty)) + } return Ok(InstSeq(vec![], reg, ty)); } let off = self.get_new_reg(); @@ -451,7 +474,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::Live(reg), // Add the offset to the stack frame pointer. lir::Inst::Load(reg, ptr, self.size_of_local(index) as lir::Sz), - lir::Inst::Dead(off), + lir::Inst::Dead(ptr), ], reg, self.type_of_local(index))) }, (&mir::Lvalue::Static(ref def), _) => { @@ -512,6 +535,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } mir::ProjectionElem::Field(ref field, ref ty) => { // Given a type, generate a field access for this type. + let struct_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; match lvctx { LvalueCtx::Value => { let mut insts = base_insts; @@ -525,7 +549,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { insts.append(&mut vec![ lir::Inst::Live(imm), - lir::Inst::CopyImm(imm, self.offset_of(base_ty, field)? as lir::Imm, size as lir::Sz), + lir::Inst::CopyImm(imm, self.offset_of(struct_ty, field)? as lir::Imm, size as lir::Sz), lir::Inst::Live(addr), lir::Inst::Add(addr, base_reg, imm), lir::Inst::Dead(imm), @@ -548,7 +572,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { insts.append(&mut vec![ lir::Inst::Live(imm), - lir::Inst::CopyImm(imm, self.offset_of(base_ty, field)? as lir::Imm, size as lir::Sz), + lir::Inst::CopyImm(imm, self.offset_of(struct_ty, field)? as lir::Imm, size as lir::Sz), lir::Inst::Live(reg), lir::Inst::Add(reg, base_reg, imm), lir::Inst::Dead(imm), @@ -561,7 +585,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } mir::ProjectionElem::Index(ref operand) => { // base [ operand ] - let InstSeq(idx_insts, idx_reg, idx_ty) = + let InstSeq(idx_insts, idx_reg, _idx_ty) = self.operand(operand).or_else( report_nyi!("mir::ProjectionElem::Index({:?})", operand))?; @@ -592,7 +616,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let res_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; let elem_sz = match layout { - &ty::layout::Layout::Array { align, element_size, .. } => + &ty::layout::Layout::Array { element_size, .. } => element_size.bytes() as lir::Sz, _ => { return Err(Error::UnexpectedIndexBase); @@ -608,6 +632,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let imm = self.get_new_reg(); let mul = self.get_new_reg(); let reg = self.get_new_reg(); + let val = self.get_new_reg(); let mut insts = idx_insts; insts.append(&mut vec![ lir::Inst::Live(imm), @@ -622,9 +647,11 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::Add(reg, base_reg, mul), lir::Inst::Dead(base_reg), lir::Inst::Dead(mul), + lir::Inst::Load(val, reg, elem_sz), + lir::Inst::Dead(reg), ]); - Ok(InstSeq(insts, reg, res_ty)) + Ok(InstSeq(insts, val, res_ty)) } LvalueCtx::RefSlice => Err(Error::UnexpectedRefSliceCtx), LvalueCtx::Address => Err(Error::NYI), @@ -675,7 +702,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } mir::Rvalue::Len(ref lvalue) => { - let InstSeq(mut lv_insts, lv_reg, lv_ty) = + let InstSeq(lv_insts, lv_reg, lv_ty) = self.lvalue(lvalue, LvalueCtx::RefSlice).or_else( report_nyi!("mir::Rvalue::Len({:?})", lvalue))?; @@ -795,7 +822,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { Ok(InstSeq(insts, reg, ty)) } mir::Rvalue::Aggregate(ref kind, ref operands) => { - let mut operands : Vec<_> = { + let operands : Vec<_> = { let mut res = Vec::with_capacity(operands.len()); for op in operands { res.push( @@ -834,11 +861,10 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ty::layout::Layout::Univariant { .. } => (), ty::layout::Layout::General { discr, .. } => { // Initialize the discriminant - let (dty, sz) = match discr { + let (_dty, sz) = match discr { ty::layout::Integer::I8 => (self.tcx.types.i8, 1), _ => { unreachable!("ty: {:?} / layout: {:?}", ty, layout); - return Err(Error::NYI) } }; @@ -883,17 +909,38 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } mir::Operand::Constant(ref constant) => { let res = match constant.literal.clone() { - mir::Literal::Item { def_id, substs } => { + mir::Literal::Item { .. /*def_id, substs*/ } => { Err(Error::NYI) }, mir::Literal::Value { ref value } if self.constval_use_static(value) => { - // Create a fake Rvalue which can be added in the - // list of statics. - let rv = mir::Rvalue::Use(operand.clone()); // Load the static in a register. let reg = self.get_new_reg(); - let ty = constant.ty; + let ty = { + if constant.ty.is_fn() { + // TyFnDecl has a 0-size, we want to save the + // address of the function, which has a + // pointer-size. + self.tcx.mk_fn_ptr(constant.ty.fn_sig(self.tcx)) + } else { + constant.ty + } + }; + + // Create a fake Rvalue which can be added in the + // list of statics. + let rv = { + if let ty::TyFnDef(_, _) = constant.ty.sty { + mir::Rvalue::Cast( + mir::CastKind::ReifyFnPointer, + operand.clone(), + ty + ) + } else { + mir::Rvalue::Use(operand.clone()) + } + }; + let (off, sz) = self.register_static(&rv, ty)?; Ok(InstSeq(vec![ lir::Inst::Live(reg), @@ -941,9 +988,6 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ConstVal::Integral(ConstInt::Usize(ConstUsize::Us64(i))) => vec![ lir::Inst::CopyImm(reg, i as lir::Imm, size) ], - ConstVal::Integral(ConstInt::Usize(ConstUsize::Us64(i))) => vec![ - lir::Inst::CopyImm(reg, i as lir::Imm, size) - ], ConstVal::Function(_, _) | ConstVal::Str(_) => unreachable!("constval use static: cv: {:?} / ty: {:?}", cv, ty), @@ -958,7 +1002,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> Result { - let &mir::Terminator{ ref source_info, ref kind } = terminator; + let &mir::Terminator{ /* ref source_info,*/ ref kind, .. } = terminator; match kind { &mir::TerminatorKind::Resume => { Ok(TermSeq(vec![], lir::Terminator::Unwind)) @@ -991,7 +1035,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ref values, ref targets, } => { - let InstSeq(idx_insts, idx_reg, idx_ty) = + let InstSeq(idx_insts, idx_reg, _idx_ty) = self.operand(discr).or_else( report_nyi!("mir::TerminatorKind::SwitchInt(discr: {:?})", discr))?; let range : lir::RangeInclusive = { @@ -1022,22 +1066,30 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ref destination, ref cleanup, } => { - let InstSeq(mut insts, fun_reg, fun_ty) = + let InstSeq(mut insts, fun_reg, _fun_ty) = self.operand(func).or_else( report_nyi!("mir::TerminatorKind::Call(func: {:?})", func))?; + // TODO: Follow what is done in librustc_trans/abi.rs to + // choose wether arguments are being transfered by value or + // by reference. + // TODO: collect the ABI with: // let sig = ty.fn_sig(self.tcx) // let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig); // let abi = sig.abi; - let mut args_reg = Vec::with_capacity(args.len()); + let mut args_reg = Vec::with_capacity(args.len() + 1); for arg in args { - let InstSeq(mut arg_insts, arg_reg, _) = + let InstSeq(mut arg_insts, arg_reg, arg_ty) = self.operand(arg).or_else( report_nyi!("mir::TerminatorKind::Call(args: [.. {:?} ..])", arg))?; - insts.append(&mut arg_insts); - args_reg.push(arg_reg); + let (_, size) = self.add_type(arg_ty, 0)?; + // 0-size arguments are ignored. + if size != 0 { + insts.append(&mut arg_insts); + args_reg.push(arg_reg); + } } let unwind_target = match cleanup { @@ -1049,30 +1101,65 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { &Some((ref lvalue, ref bb)) => { let resume = self.new_block(); let target = self.get_block(*bb); - let InstSeq(mut lv_insts, lv_reg, lv_ty) = + let InstSeq(mut ret_insts, ret_reg, ret_ty) = self.lvalue(lvalue, LvalueCtx::Address).or_else( report_nyi!("Call(destination: {:?}, _)", lvalue))?; + let ret_ty = ret_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; - let (_, size) = self.add_type(lv_ty, 0)?; - let lv_reg_def = (lv_reg, size); - - // Create a basic block in which the calls returns, - // and in which we set the lvalue with the returned - // value of the call. - let fp_reg = self.fp; - self.blocks[resume] = lir::BasicBlockData { - input_regs: vec![ - (fp_reg, 8 /* size of frame ptr */ ), - lv_reg_def - ], - output_regs: vec![ fp_reg ], - insts: lv_insts, - end: lir::Terminator::Goto { - target: target, - }, + // TODO: check that the returned value is the same + // size as the lvalue storage space. + let (_, size) = self.add_type(ret_ty, 0)?; + + let layout = ret_ty.layout(self.tcx, self.param_env)?; + let is_fat_ptr = match layout { + &ty::layout::Layout::FatPointer{ .. } => true, + _ => false, }; - Some(( Some(lv_reg_def), resume )) + if size <= 8 || is_fat_ptr { + // The returned values is returned by value, + // and fat-pointer are returned on 2 + // registers. + let val_reg = self.get_new_reg(); + let ret_reg_def = (val_reg, size); + ret_insts.append(&mut vec![ + lir::Inst::Store(ret_reg, val_reg, size), + lir::Inst::Dead(val_reg), + lir::Inst::Dead(ret_reg), + ]); + + // Create a basic block in which the calls returns, + // and in which we set the lvalue with the returned + // value of the call. + let fp_reg = self.fp; + self.blocks[resume] = lir::BasicBlockData { + input_regs: vec![ + (fp_reg, 8 /* size of frame ptr */ ), + ret_reg_def + ], + output_regs: vec![ fp_reg ], + insts: ret_insts, + end: lir::Terminator::Goto { + target: target, + }, + }; + + Some(( Some(ret_reg_def), resume )) + } else { + // The address where the content should be + // initialized is given as the first + // argument of the function, and should be + // initialized by the callee. + // + // Note that the call will also return the + // same value, but we do not yet care about + // making it more efficient to read, thus we + // simply ignore the returned value for now. + insts.append(&mut ret_insts); + args_reg.insert(0, ret_reg); + + Some(( None, target )) + } } &None => None, }; @@ -1085,7 +1172,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { })) } &mir::TerminatorKind::Drop { ref location, target, unwind } => { - let InstSeq(mut lv_insts, lv_reg, lv_ty) = + let InstSeq(lv_insts, lv_reg, lv_ty) = self.lvalue(location, LvalueCtx::Address).or_else( report_nyi!("Drop(location: {:?}, _)", location))?; @@ -1094,10 +1181,15 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let substs = self.tcx.intern_substs(&[ty::subst::Kind::from(lv_ty)]); // see def_ty let ty = self.tcx.type_of(def_id).subst(self.tcx, substs); + assert!(ty.is_fn()); + let ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); // Add the drop_in_place function address as a reference. - let rv = mir::Rvalue::Use( - mir::Operand::function_handle(self.tcx, def_id, substs, Default::default())); + let rv = mir::Rvalue::Cast( + mir::CastKind::ReifyFnPointer, + mir::Operand::function_handle(self.tcx, def_id, substs, Default::default()), + ty + ); let (off, sz) = self.register_static(&rv, ty)?; let fun_reg = self.get_new_reg(); @@ -1117,7 +1209,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { &mir::TerminatorKind::Assert { ref cond, expected, target, .. } => { - let InstSeq(mut cond_insts, cond_reg, cond_ty) = + let InstSeq(cond_insts, cond_reg, _cond_ty) = self.operand(cond).or_else( report_nyi!("Assert(cond: {:?}, _)", cond))?;