From f738e96df6969b786fe6b8650e22f5ee19f6d940 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Tue, 14 Apr 2026 20:05:36 -0400 Subject: [PATCH] goalc/arm: PC relative loads/stores, and simple moves --- goalc/emitter/IGen.cpp | 34 ++--- goalc/emitter/IGenARM64.cpp | 256 ++++++++++++++++++++++++++---------- goalc/emitter/IGenARM64.h | 34 ++--- goalc/emitter/Instruction.h | 41 ++++-- goalc/emitter/Register.h | 7 +- 5 files changed, 257 insertions(+), 115 deletions(-) diff --git a/goalc/emitter/IGen.cpp b/goalc/emitter/IGen.cpp index f59b12ca84..99fad99b57 100644 --- a/goalc/emitter/IGen.cpp +++ b/goalc/emitter/IGen.cpp @@ -32,23 +32,23 @@ Instruction mov_gpr64_s32(const ObjectGenerator& gen, Register dst, int64_t val) } Instruction movd_gpr32_xmm32(const ObjectGenerator& gen, Register dst, Register src) { - IGEN_DISPATCH(movd_gpr32_xmm32, dst, src); + IGEN_DISPATCH(movd_gpr32_f32, dst, src); } Instruction movd_xmm32_gpr32(const ObjectGenerator& gen, Register dst, Register src) { - IGEN_DISPATCH(movd_xmm32_gpr32, dst, src); + IGEN_DISPATCH(movd_f32_gpr32, dst, src); } Instruction movq_gpr64_xmm64(const ObjectGenerator& gen, Register dst, Register src) { - IGEN_DISPATCH(movq_gpr64_xmm64, dst, src); + IGEN_DISPATCH(movq_gpr64_f64, dst, src); } Instruction movq_xmm64_gpr64(const ObjectGenerator& gen, Register dst, Register src) { - IGEN_DISPATCH(movq_xmm64_gpr64, dst, src); + IGEN_DISPATCH(movq_f64_gpr64, dst, src); } Instruction mov_xmm32_xmm32(const ObjectGenerator& gen, Register dst, Register src) { - IGEN_DISPATCH(mov_xmm32_xmm32, dst, src); + IGEN_DISPATCH(mov_f32_f32, dst, src); } Instruction load8s_gpr64_gpr64_plus_gpr64(const ObjectGenerator& gen, @@ -516,31 +516,31 @@ Instruction store128_xmm128_reg_offset(const ObjectGenerator& gen, } Instruction load64_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load64_rip_s32, dest, offset); + IGEN_DISPATCH(load64_pcRel_s32, dest, offset); } Instruction load32s_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load32s_rip_s32, dest, offset); + IGEN_DISPATCH(load32s_pcRel_s32, dest, offset); } Instruction load32u_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load32u_rip_s32, dest, offset); + IGEN_DISPATCH(load32u_pcRel_s32, dest, offset); } Instruction load16u_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load16u_rip_s32, dest, offset); + IGEN_DISPATCH(load16u_pcRel_s32, dest, offset); } Instruction load16s_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load16s_rip_s32, dest, offset); + IGEN_DISPATCH(load16s_pcRel_s32, dest, offset); } Instruction load8u_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load8u_rip_s32, dest, offset); + IGEN_DISPATCH(load8u_pcRel_s32, dest, offset); } Instruction load8s_rip_s32(const ObjectGenerator& gen, Register dest, s64 offset) { - IGEN_DISPATCH(load8s_rip_s32, dest, offset); + IGEN_DISPATCH(load8s_pcRel_s32, dest, offset); } Instruction static_load(const ObjectGenerator& gen, @@ -552,19 +552,19 @@ Instruction static_load(const ObjectGenerator& gen, } Instruction store64_rip_s32(const ObjectGenerator& gen, Register src, s64 offset) { - IGEN_DISPATCH(store64_rip_s32, src, offset); + IGEN_DISPATCH(store64_pcRel_s32, src, offset); } Instruction store32_rip_s32(const ObjectGenerator& gen, Register src, s64 offset) { - IGEN_DISPATCH(store32_rip_s32, src, offset); + IGEN_DISPATCH(store32_pcRel_s32, src, offset); } Instruction store16_rip_s32(const ObjectGenerator& gen, Register src, s64 offset) { - IGEN_DISPATCH(store16_rip_s32, src, offset); + IGEN_DISPATCH(store16_pcRel_s32, src, offset); } Instruction store8_rip_s32(const ObjectGenerator& gen, Register src, s64 offset) { - IGEN_DISPATCH(store8_rip_s32, src, offset); + IGEN_DISPATCH(store8_pcRel_s32, src, offset); } Instruction static_store(const ObjectGenerator& gen, Register value, s64 offset, int size) { @@ -576,7 +576,7 @@ Instruction static_addr(const ObjectGenerator& gen, Register dst, s64 offset) { } Instruction static_load_xmm32(const ObjectGenerator& gen, Register simd_dest, s64 offset) { - IGEN_DISPATCH(static_load_xmm32, simd_dest, offset); + IGEN_DISPATCH(static_load_fp32, simd_dest, offset); } Instruction static_store_xmm32(const ObjectGenerator& gen, Register xmm_value, s64 offset) { diff --git a/goalc/emitter/IGenARM64.cpp b/goalc/emitter/IGenARM64.cpp index 846e87acbb..5a2cf20304 100644 --- a/goalc/emitter/IGenARM64.cpp +++ b/goalc/emitter/IGenARM64.cpp @@ -23,8 +23,7 @@ using namespace emitter::ARM64; InstructionARM64 mov_gpr64_gpr64(Register dst, Register src) { // https://www.scs.stanford.edu/~zyedidia/arm64/mov_orr_log_shift.html - // sf 0 1 0 1 0 1 0 0 0 0 Rm 0 - // 0 0 0 0 0 1 1 1 1 1 Rd MOV , + // 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()), @@ -32,50 +31,79 @@ InstructionARM64 mov_gpr64_gpr64(Register dst, Register src) { } InstructionARM64 mov_gpr64_u64(Register dst, uint64_t val) { - // TODO - cannot be done in a single arm64 instruction - // multiple https://www.scs.stanford.edu/~zyedidia/arm64/movk.html are needed - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); + // 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) { - // TODO - cannot be done in a single arm64 instruction - // multiple https://www.scs.stanford.edu/~zyedidia/arm64/movk.html are needed - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); + return mov_gpr64_u64(dst, val); } InstructionARM64 mov_gpr64_s32(Register dst, int64_t val) { - // TODO - cannot be done in a single arm64 instruction - // multiple https://www.scs.stanford.edu/~zyedidia/arm64/movk.html are needed - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); + // 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); } -// TODO - should these be make generic to simdX? -InstructionARM64 movd_gpr32_xmm32(Register dst, Register src) { - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); +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_xmm32_gpr32(Register dst, Register src) { - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); +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_xmm64(Register dst, Register src) { - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); +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_xmm64_gpr64(Register dst, Register src) { - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); +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_xmm32_xmm32(Register dst, Register src) { - ASSERT_MSG(false, "not yet implemented"); - return InstructionARM64(0b0); +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())); } // todo - GPR64 -> XMM64 (zext) @@ -505,84 +533,180 @@ InstructionARM64 store128_xmm128_reg_offset(Register base, Register xmm_val, s64 } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -// RIP loads and stores +// PC relative loads and stores //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -InstructionARM64 load64_rip_s32(Register dest, s64 offset) { +// 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 ,