refactor(backend): move memory/globals/conversion/pointer handlers into ops.zig (A7.4 slice b)
Relocate the `// ── Memory ──`, `// ── Globals ──`, `// ── Conversions ──`, and `// ── Pointer ops ──` opcode handler bodies out of `emitInst` in src/ir/emit_llvm.zig into the existing `Ops` facade in src/backend/llvm/ops.zig. Each `emitInst` arm now delegates via `self.ops().emit<Op>(...)`. Widen `emitConversion`, `coerceArg`, and `getRefIRType` to `pub` (the only helpers the moved bodies call). Pure relocation: zero snapshot churn.
This commit is contained in:
@@ -9,16 +9,22 @@ const LLVMEmitter = emit.LLVMEmitter;
|
||||
const Inst = ir_inst.Inst;
|
||||
const BinOp = ir_inst.BinOp;
|
||||
const UnaryOp = ir_inst.UnaryOp;
|
||||
const Store = ir_inst.Store;
|
||||
const Conversion = ir_inst.Conversion;
|
||||
const GlobalId = ir_inst.GlobalId;
|
||||
const GlobalSet = ir_inst.GlobalSet;
|
||||
const FuncId = ir_inst.FuncId;
|
||||
const TypeId = ir_types.TypeId;
|
||||
const StringId = ir_types.StringId;
|
||||
|
||||
/// Scalar instruction-emission handlers for `emitInst`: the constant,
|
||||
/// arithmetic, bitwise, comparison, and logical opcodes. A backend
|
||||
/// `*LLVMEmitter` facade (field `e`): each method emits one opcode's LLVM IR via
|
||||
/// `self.e.*`. The shared infra these bodies call back into
|
||||
/// Instruction-emission handlers for `emitInst`: the constant, arithmetic,
|
||||
/// bitwise, comparison, logical, memory, globals, conversion, and pointer
|
||||
/// opcodes. A backend `*LLVMEmitter` facade (field `e`): each method emits one
|
||||
/// opcode's LLVM IR via `self.e.*`. The shared infra these bodies call back into
|
||||
/// (`mapRef`/`resolveRef`/`matchBinOpTypes`/`emitCmp`/`emitCmpOrdered`/
|
||||
/// `emitStrCmp`/`emitStringConstant`/`reflection`) stays on `LLVMEmitter`.
|
||||
/// `emitInst`'s scalar arms reach these via `self.ops()`.
|
||||
/// `emitStrCmp`/`emitStringConstant`/`reflection`/`emitConversion`/`coerceArg`/
|
||||
/// `getRefIRType`) stays on `LLVMEmitter`. `emitInst`'s arms reach these via
|
||||
/// `self.ops()`.
|
||||
pub const Ops = struct {
|
||||
e: *LLVMEmitter,
|
||||
|
||||
@@ -286,4 +292,162 @@ pub const Ops = struct {
|
||||
const operand = self.e.resolveRef(un.operand);
|
||||
self.e.mapRef(c.LLVMBuildNot(self.e.builder, operand, "lnot"));
|
||||
}
|
||||
|
||||
// ── Memory ────────────────────────────────────────────
|
||||
pub fn emitAlloca(self: Ops, elem_ty: TypeId) void {
|
||||
const llvm_ty = self.e.toLLVMType(elem_ty);
|
||||
const result = c.LLVMBuildAlloca(self.e.builder, llvm_ty, "alloca");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitLoad(self: Ops, instruction: *const Inst, un: UnaryOp) void {
|
||||
const ptr = self.e.resolveRef(un.operand);
|
||||
const ptr_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(ptr));
|
||||
if (ptr_kind == c.LLVMPointerTypeKind and instruction.ty != .void) {
|
||||
const llvm_ty = self.e.toLLVMType(instruction.ty);
|
||||
const result = c.LLVMBuildLoad2(self.e.builder, llvm_ty, ptr, "load");
|
||||
self.e.mapRef(result);
|
||||
} else {
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.toLLVMType(if (instruction.ty == .void) .s64 else instruction.ty)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitStore(self: Ops, st: Store) void {
|
||||
const ptr = self.e.resolveRef(st.ptr);
|
||||
var val = self.e.resolveRef(st.val);
|
||||
// Guard: don't store void types or store to non-pointer
|
||||
const ptr_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(ptr));
|
||||
const val_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(val));
|
||||
if (ptr_kind == c.LLVMPointerTypeKind and val_kind != c.LLVMVoidTypeKind) {
|
||||
// Coerce value to match the IR-declared pointer target type.
|
||||
// E.g. storing i64 to *i8 (from index_gep on string) needs truncation.
|
||||
//
|
||||
// Only unwrap .pointer (from index_gep/alloca: *element → element).
|
||||
// Never unwrap .many_pointer — it only appears as struct_gep field
|
||||
// value types (e.g., [*]BigNode), where unwrapping to the element
|
||||
// type gives a wrong store size (stores BigNode-sized instead of ptr).
|
||||
if (self.e.getRefIRType(st.ptr)) |ptr_ir_ty| {
|
||||
const pointee_info = self.e.ir_mod.types.get(ptr_ir_ty);
|
||||
const target_ty: ?c.LLVMTypeRef = switch (pointee_info) {
|
||||
.pointer => |p| self.e.toLLVMType(p.pointee),
|
||||
else => null,
|
||||
};
|
||||
if (target_ty) |tt| {
|
||||
val = self.e.coerceArg(val, tt);
|
||||
}
|
||||
}
|
||||
_ = c.LLVMBuildStore(self.e.builder, val, ptr);
|
||||
}
|
||||
self.e.advanceRefCounter();
|
||||
}
|
||||
|
||||
// ── Globals ───────────────────────────────────────────
|
||||
pub fn emitGlobalGet(self: Ops, instruction: *const Inst, gid: GlobalId) void {
|
||||
const llvm_global = self.e.global_map.get(gid.index()) orelse {
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.toLLVMType(instruction.ty)));
|
||||
return;
|
||||
};
|
||||
const llvm_ty = self.e.toLLVMType(instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildLoad2(self.e.builder, llvm_ty, llvm_global, "gload"));
|
||||
}
|
||||
|
||||
pub fn emitGlobalAddr(self: Ops, gid: GlobalId) void {
|
||||
const llvm_global = self.e.global_map.get(gid.index()) orelse {
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.cached_ptr));
|
||||
return;
|
||||
};
|
||||
// Return the global's address directly (no load)
|
||||
self.e.mapRef(llvm_global);
|
||||
}
|
||||
|
||||
pub fn emitFuncRef(self: Ops, fid: FuncId) void {
|
||||
// Produce a reference to the function as a function pointer value
|
||||
if (self.e.func_map.get(@intFromEnum(fid))) |llvm_func| {
|
||||
self.e.mapRef(llvm_func);
|
||||
} else {
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.cached_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitGlobalSet(self: Ops, gs: GlobalSet) void {
|
||||
const llvm_global = self.e.global_map.get(gs.global.index()) orelse {
|
||||
self.e.advanceRefCounter();
|
||||
return;
|
||||
};
|
||||
const val = self.e.resolveRef(gs.value);
|
||||
_ = c.LLVMBuildStore(self.e.builder, val, llvm_global);
|
||||
self.e.advanceRefCounter();
|
||||
}
|
||||
|
||||
// ── Conversions ───────────────────────────────────────
|
||||
pub fn emitWiden(self: Ops, conv: Conversion) void {
|
||||
const operand = self.e.resolveRef(conv.operand);
|
||||
const to_ty = self.e.toLLVMType(conv.to);
|
||||
const result = self.e.emitConversion(operand, conv.from, conv.to, to_ty);
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitNarrow(self: Ops, conv: Conversion) void {
|
||||
const operand = self.e.resolveRef(conv.operand);
|
||||
const to_ty = self.e.toLLVMType(conv.to);
|
||||
const result = self.e.emitConversion(operand, conv.from, conv.to, to_ty);
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitBitcast(self: Ops, conv: Conversion) void {
|
||||
const operand = self.e.resolveRef(conv.operand);
|
||||
const to_ty = self.e.toLLVMType(conv.to);
|
||||
// LLVMBuildBitCast doesn't accept ptr↔int on modern
|
||||
// LLVM. Dispatch to PtrToInt / IntToPtr when needed —
|
||||
// lower.zig emits a `bitcast` IR op for both shapes.
|
||||
const from_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(operand));
|
||||
const to_kind = c.LLVMGetTypeKind(to_ty);
|
||||
if (from_kind == c.LLVMPointerTypeKind and to_kind == c.LLVMIntegerTypeKind) {
|
||||
const i64_val = c.LLVMBuildPtrToInt(self.e.builder, operand, self.e.cached_i64, "pti");
|
||||
const w = c.LLVMGetIntTypeWidth(to_ty);
|
||||
if (w == 64) {
|
||||
self.e.mapRef(i64_val);
|
||||
} else if (w < 64) {
|
||||
self.e.mapRef(c.LLVMBuildTrunc(self.e.builder, i64_val, to_ty, "pti.tr"));
|
||||
} else {
|
||||
self.e.mapRef(c.LLVMBuildZExt(self.e.builder, i64_val, to_ty, "pti.ext"));
|
||||
}
|
||||
} else if (from_kind == c.LLVMIntegerTypeKind and to_kind == c.LLVMPointerTypeKind) {
|
||||
self.e.mapRef(c.LLVMBuildIntToPtr(self.e.builder, operand, to_ty, "itp"));
|
||||
} else {
|
||||
self.e.mapRef(c.LLVMBuildBitCast(self.e.builder, operand, to_ty, "bitcast"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitIntToFloat(self: Ops, conv: Conversion) void {
|
||||
const operand = self.e.resolveRef(conv.operand);
|
||||
const to_ty = self.e.toLLVMType(conv.to);
|
||||
const result = if (emit.isSignedType(conv.from))
|
||||
c.LLVMBuildSIToFP(self.e.builder, operand, to_ty, "sitofp")
|
||||
else
|
||||
c.LLVMBuildUIToFP(self.e.builder, operand, to_ty, "uitofp");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitFloatToInt(self: Ops, conv: Conversion) void {
|
||||
const operand = self.e.resolveRef(conv.operand);
|
||||
const to_ty = self.e.toLLVMType(conv.to);
|
||||
const result = if (emit.isSignedType(conv.to))
|
||||
c.LLVMBuildFPToSI(self.e.builder, operand, to_ty, "fptosi")
|
||||
else
|
||||
c.LLVMBuildFPToUI(self.e.builder, operand, to_ty, "fptoui");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
// ── Pointer ops ───────────────────────────────────────
|
||||
pub fn emitAddrOf(self: Ops, un: UnaryOp) void {
|
||||
// addr_of returns the pointer directly (the operand is already a ptr from alloca)
|
||||
self.e.mapRef(self.e.resolveRef(un.operand));
|
||||
}
|
||||
|
||||
pub fn emitDeref(self: Ops, instruction: *const Inst, un: UnaryOp) void {
|
||||
const ptr = self.e.resolveRef(un.operand);
|
||||
const llvm_ty = self.e.toLLVMType(instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildLoad2(self.e.builder, llvm_ty, ptr, "deref"));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user