#include "IGenARM64.h" #include #include "goalc/emitter/Instruction.h" #include "goalc/emitter/InstructionSet.h" #include "goalc/emitter/Register.h" // https://armconverter.com/?code=ret // https://developer.arm.com/documentation/ddi0487/latest // TODO ARM64 - just silencing errors while things are not implemented obviously #pragma GCC diagnostic ignored "-Wunused-parameter" namespace emitter { namespace IGen { namespace ARM64 { //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // MOVES //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; const auto instr_set = emitter::InstructionSet::ARM64; using namespace emitter::ARM64; InstructionARM64 mov_gpr64_gpr64(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/mov_orr_log_shift.html // MOV , ASSERT(dst.is_gpr(instr_set)); ASSERT(src.is_gpr(instr_set)); return InstructionARM64(Base(0b10101010000, 11), Rm(src.id()), Rn(0b11111), Rd(dst.id()), Imm6(0)); } InstructionARM64 mov_gpr64_u64(Register dst, uint64_t val) { // Cannot be done in a single instruction, must combine multiple MOVZ/MOVKs std::vector instrs; bool emitted_movz = false; for (int i = 0; i < 4; i++) { u16 chunk = (val >> (i * 16)) & 0xFFFF; if (!emitted_movz && chunk != 0) { // https://www.scs.stanford.edu/~zyedidia/arm64/movz.html // MOVZ , #{, LSL #/16} instrs.emplace_back( InstructionARM64(Base(0b110100101, 9), Hw(i), Imm16(chunk), Rd(dst.id()))); emitted_movz = true; } else if (emitted_movz && chunk != 0) { // https://www.scs.stanford.edu/~zyedidia/arm64/movk.html // MOVK , #{, LSL #/16} instrs.emplace_back( InstructionARM64(Base(0b111100101, 9), Hw(i), Imm16(chunk), Rd(dst.id()))); } } if (!emitted_movz) { // https://www.scs.stanford.edu/~zyedidia/arm64/movz.html // MOVZ , #{, LSL #0} instrs.emplace_back(InstructionARM64(Base(0b110100101, 9), Hw(0), Imm16(0), Rd(dst.id()))); } return InstructionARM64(instrs); } InstructionARM64 mov_gpr64_u32(Register dst, uint64_t val) { return mov_gpr64_u64(dst, val); } InstructionARM64 mov_gpr64_s32(Register dst, int64_t val) { // preserve sign -- but we are are simply moving the bits u64 raw_val = static_cast(val); // via int64_t → sign already there return mov_gpr64_u64(dst, raw_val); } InstructionARM64 movd_gpr32_f32(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/fmov_float_gen.html // Single-precision to 32-bit (sf == 0 && ftype == 00 && rmode == 00 && opcode == 110) // FMOV , ASSERT(dst.is_gpr(instr_set)); return InstructionARM64(Base(0b0001111000100110000000, 22), Rn(src.id()), Rd(dst.id())); } InstructionARM64 movd_f32_gpr32(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/fmov_float_gen.html // 32-bit to single-precision (sf == 0 && ftype == 00 && rmode == 00 && opcode == 111) // FMOV , ASSERT(src.is_gpr(instr_set)); return InstructionARM64(Base(0b0001111000100111000000, 22), Rn(src.id()), Rd(dst.id())); } InstructionARM64 movq_gpr64_f64(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/fmov_float_gen.html // Double-precision to 64-bit (sf == 1 && ftype == 01 && rmode == 00 && opcode == 110) // FMOV , ASSERT(dst.is_gpr(instr_set)); return InstructionARM64(Base(0b1001111001100110000000, 22), Rn(src.id()), Rd(dst.id())); } InstructionARM64 movq_f64_gpr64(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/fmov_float_gen.html // 64-bit to double-precision (sf == 1 && ftype == 01 && rmode == 00 && opcode == 111) // FMOV , ASSERT(src.is_gpr(instr_set)); return InstructionARM64(Base(0b1001111001100111000000, 22), Rn(src.id()), Rd(dst.id())); } InstructionARM64 mov_f32_f32(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/fmov_float.html // Single-precision (ftype == 00) // FMOV , return InstructionARM64(Base(0b0001111000100000010000, 22), Rn(src.id()), Rd(dst.id())); } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // GOAL Loads and Stores //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; InstructionARM64 load8s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { // https://www.scs.stanford.edu/~zyedidia/arm64/ldrsb_reg.html // 64-bit with extended register offset (opc == 10 && option != 011) // LDRSB , [, (|), {}] ASSERT(dst.is_gpr(instr_set)); ASSERT(addr1.is_gpr(instr_set)); ASSERT(addr2.is_gpr(instr_set)); return InstructionARM64(Base(0b0011100010100000111010, 22), Rt(dst.id()), Rn(addr1.id()), Rm(addr2.id())); } InstructionARM64 store8_gpr64_gpr64_plus_gpr64(Register addr1, Register addr2, Register value) { // https://www.scs.stanford.edu/~zyedidia/arm64/strb_reg.html ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load8s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store8_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load8s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store8_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load8u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load8u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load8u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store16_gpr64_gpr64_plus_gpr64(Register addr1, Register addr2, Register value) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store16_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store16_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load16u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_gpr64_gpr64_plus_gpr64(Register addr1, Register addr2, Register value) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load64_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store64_gpr64_gpr64_plus_gpr64(Register addr1, Register addr2, Register value) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load64_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store64_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load64_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store64_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, Register addr2, Register value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store_goal_vf(Register addr, Register value, Register off, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store_goal_gpr(Register addr, Register value, Register off, int offset, int size) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load_goal_xmm128(Register dst, Register addr, Register off, int offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load_goal_gpr(Register dst, Register addr, Register off, int offset, int size, bool sign_extend) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // LOADS n' STORES - XMM32 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; InstructionARM64 store32_xmm32_gpr64_plus_gpr64(Register addr1, Register addr2, Register xmm_value) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32_xmm32_gpr64_plus_gpr64(Register simd_dest, Register addr1, Register addr2) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_xmm32_gpr64_plus_gpr64_plus_s8(Register addr1, Register addr2, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32_xmm32_gpr64_plus_gpr64_plus_s8(Register simd_dest, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_xmm32_gpr64_plus_gpr64_plus_s32(Register addr1, Register addr2, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 lea_reg_plus_off32(Register dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 lea_reg_plus_off8(Register dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 lea_reg_plus_off(Register dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_xmm32_gpr64_plus_s32(Register base, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store32_xmm32_gpr64_plus_s8(Register base, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32_xmm32_gpr64_plus_gpr64_plus_s32(Register simd_dest, Register addr1, Register addr2, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32_xmm32_gpr64_plus_s32(Register simd_dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load32_xmm32_gpr64_plus_s8(Register simd_dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load_goal_xmm32(Register simd_dest, Register addr, Register off, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store_goal_xmm32(Register addr, Register xmm_value, Register off, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store_reg_offset_xmm32(Register base, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load_reg_offset_xmm32(Register simd_dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // LOADS n' STORES - SIMD (128-bit, QWORDS) //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; InstructionARM64 store128_gpr64_simd128(Register gpr_addr, Register simd_reg) { // https://www.scs.stanford.edu/~zyedidia/arm64/str_imm_fpsimd.html // - STR Qn, [Xn] (unsigned offset) ASSERT(gpr_addr.is_gpr(instr_set)); ASSERT( simd_reg.is_128bit_simd(instr_set)); // TODO ARM64 - this assertion isn't as useful for ARM // since Q registers are not unique in terms of their id return InstructionARM64(Base(0b0011110110, 10), Rn(gpr_addr.id()), Rt(simd_reg.id()), Imm12(0)); } InstructionARM64 store128_gpr64_simd128_s32(Register gpr_addr, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store128_gpr64_simd128_s8(Register gpr_addr, Register xmm_value, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load128_simd128_gpr64(Register simd_dest, Register gpr_addr) { // https://www.scs.stanford.edu/~zyedidia/arm64/ldr_imm_fpsimd.html // - LDR , [{, #}] ASSERT(gpr_addr.is_gpr(instr_set)); ASSERT(simd_dest.is_128bit_simd( instr_set)); // TODO ARM64 - this assertion isn't as useful for ARM // since Q registers are not unique in terms of their id return InstructionARM64(Base(0b0011110111, 10), Rn(gpr_addr.id()), Rt(simd_dest.id()), Imm12(0)); } InstructionARM64 load128_simd128_gpr64_s32(Register simd_dest, Register gpr_addr, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load128_simd128_gpr64_s8(Register simd_dest, Register gpr_addr, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 load128_xmm128_reg_offset(Register simd_dest, Register base, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } InstructionARM64 store128_xmm128_reg_offset(Register base, Register xmm_val, s64 offset) { ASSERT_MSG(false, "not yet implemented"); return InstructionARM64(0b0); } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // PC relative loads and stores //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // Implement with LDR but that has a 1MB range limit on ARM (not 2GB like on x86) // Hopefully this is fine, however it could potentially not be if this is loading static data, which // may not within 1MB of the current instruction -- that all depends on the linker layout. // // TODO ARM - But keep it simple at first, add good assertions and we'll see what happens when we // compile for real. const int ARM64_LDR_MIN = -(1 << 18) * 4; const int ARM64_LDR_MAX = ((1 << 18) - 1) * 4; InstructionARM64 load64_pcRel_s32(Register dest, s64 offset) { ASSERT(dest.is_gpr(instr_set)); ASSERT_MSG(offset >= ARM64_LDR_MIN && offset <= ARM64_LDR_MAX, "PC Relative offset is too large for ARM64, fix it."); // https://www.scs.stanford.edu/~zyedidia/arm64/ldr_lit_gen.html // LDR ,