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:
@@ -1390,151 +1390,25 @@ pub const LLVMEmitter = struct {
|
||||
.bool_not => |un| self.ops().emitBoolNot(un),
|
||||
|
||||
// ── Memory ────────────────────────────────────────────
|
||||
.alloca => |elem_ty| {
|
||||
const llvm_ty = self.toLLVMType(elem_ty);
|
||||
const result = c.LLVMBuildAlloca(self.builder, llvm_ty, "alloca");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.load => |un| {
|
||||
const ptr = self.resolveRef(un.operand);
|
||||
const ptr_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(ptr));
|
||||
if (ptr_kind == c.LLVMPointerTypeKind and instruction.ty != .void) {
|
||||
const llvm_ty = self.toLLVMType(instruction.ty);
|
||||
const result = c.LLVMBuildLoad2(self.builder, llvm_ty, ptr, "load");
|
||||
self.mapRef(result);
|
||||
} else {
|
||||
self.mapRef(c.LLVMGetUndef(self.toLLVMType(if (instruction.ty == .void) .s64 else instruction.ty)));
|
||||
}
|
||||
},
|
||||
.store => |st| {
|
||||
const ptr = self.resolveRef(st.ptr);
|
||||
var val = self.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.getRefIRType(st.ptr)) |ptr_ir_ty| {
|
||||
const pointee_info = self.ir_mod.types.get(ptr_ir_ty);
|
||||
const target_ty: ?c.LLVMTypeRef = switch (pointee_info) {
|
||||
.pointer => |p| self.toLLVMType(p.pointee),
|
||||
else => null,
|
||||
};
|
||||
if (target_ty) |tt| {
|
||||
val = self.coerceArg(val, tt);
|
||||
}
|
||||
}
|
||||
_ = c.LLVMBuildStore(self.builder, val, ptr);
|
||||
}
|
||||
self.advanceRefCounter();
|
||||
},
|
||||
.alloca => |elem_ty| self.ops().emitAlloca(elem_ty),
|
||||
.load => |un| self.ops().emitLoad(instruction, un),
|
||||
.store => |st| self.ops().emitStore(st),
|
||||
// ── Globals ───────────────────────────────────────────
|
||||
.global_get => |gid| {
|
||||
const llvm_global = self.global_map.get(gid.index()) orelse {
|
||||
self.mapRef(c.LLVMGetUndef(self.toLLVMType(instruction.ty)));
|
||||
return;
|
||||
};
|
||||
const llvm_ty = self.toLLVMType(instruction.ty);
|
||||
self.mapRef(c.LLVMBuildLoad2(self.builder, llvm_ty, llvm_global, "gload"));
|
||||
},
|
||||
.global_addr => |gid| {
|
||||
const llvm_global = self.global_map.get(gid.index()) orelse {
|
||||
self.mapRef(c.LLVMGetUndef(self.cached_ptr));
|
||||
return;
|
||||
};
|
||||
// Return the global's address directly (no load)
|
||||
self.mapRef(llvm_global);
|
||||
},
|
||||
.func_ref => |fid| {
|
||||
// Produce a reference to the function as a function pointer value
|
||||
if (self.func_map.get(@intFromEnum(fid))) |llvm_func| {
|
||||
self.mapRef(llvm_func);
|
||||
} else {
|
||||
self.mapRef(c.LLVMGetUndef(self.cached_ptr));
|
||||
}
|
||||
},
|
||||
.global_set => |gs| {
|
||||
const llvm_global = self.global_map.get(gs.global.index()) orelse {
|
||||
self.advanceRefCounter();
|
||||
return;
|
||||
};
|
||||
const val = self.resolveRef(gs.value);
|
||||
_ = c.LLVMBuildStore(self.builder, val, llvm_global);
|
||||
self.advanceRefCounter();
|
||||
},
|
||||
.global_get => |gid| self.ops().emitGlobalGet(instruction, gid),
|
||||
.global_addr => |gid| self.ops().emitGlobalAddr(gid),
|
||||
.func_ref => |fid| self.ops().emitFuncRef(fid),
|
||||
.global_set => |gs| self.ops().emitGlobalSet(gs),
|
||||
|
||||
// ── Conversions ───────────────────────────────────────
|
||||
.widen => |conv| {
|
||||
const operand = self.resolveRef(conv.operand);
|
||||
const to_ty = self.toLLVMType(conv.to);
|
||||
const result = self.emitConversion(operand, conv.from, conv.to, to_ty);
|
||||
self.mapRef(result);
|
||||
},
|
||||
.narrow => |conv| {
|
||||
const operand = self.resolveRef(conv.operand);
|
||||
const to_ty = self.toLLVMType(conv.to);
|
||||
const result = self.emitConversion(operand, conv.from, conv.to, to_ty);
|
||||
self.mapRef(result);
|
||||
},
|
||||
.bitcast => |conv| {
|
||||
const operand = self.resolveRef(conv.operand);
|
||||
const to_ty = self.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.builder, operand, self.cached_i64, "pti");
|
||||
const w = c.LLVMGetIntTypeWidth(to_ty);
|
||||
if (w == 64) {
|
||||
self.mapRef(i64_val);
|
||||
} else if (w < 64) {
|
||||
self.mapRef(c.LLVMBuildTrunc(self.builder, i64_val, to_ty, "pti.tr"));
|
||||
} else {
|
||||
self.mapRef(c.LLVMBuildZExt(self.builder, i64_val, to_ty, "pti.ext"));
|
||||
}
|
||||
} else if (from_kind == c.LLVMIntegerTypeKind and to_kind == c.LLVMPointerTypeKind) {
|
||||
self.mapRef(c.LLVMBuildIntToPtr(self.builder, operand, to_ty, "itp"));
|
||||
} else {
|
||||
self.mapRef(c.LLVMBuildBitCast(self.builder, operand, to_ty, "bitcast"));
|
||||
}
|
||||
},
|
||||
.int_to_float => |conv| {
|
||||
const operand = self.resolveRef(conv.operand);
|
||||
const to_ty = self.toLLVMType(conv.to);
|
||||
const result = if (isSignedType(conv.from))
|
||||
c.LLVMBuildSIToFP(self.builder, operand, to_ty, "sitofp")
|
||||
else
|
||||
c.LLVMBuildUIToFP(self.builder, operand, to_ty, "uitofp");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.float_to_int => |conv| {
|
||||
const operand = self.resolveRef(conv.operand);
|
||||
const to_ty = self.toLLVMType(conv.to);
|
||||
const result = if (isSignedType(conv.to))
|
||||
c.LLVMBuildFPToSI(self.builder, operand, to_ty, "fptosi")
|
||||
else
|
||||
c.LLVMBuildFPToUI(self.builder, operand, to_ty, "fptoui");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.widen => |conv| self.ops().emitWiden(conv),
|
||||
.narrow => |conv| self.ops().emitNarrow(conv),
|
||||
.bitcast => |conv| self.ops().emitBitcast(conv),
|
||||
.int_to_float => |conv| self.ops().emitIntToFloat(conv),
|
||||
.float_to_int => |conv| self.ops().emitFloatToInt(conv),
|
||||
|
||||
// ── Pointer ops ───────────────────────────────────────
|
||||
.addr_of => |un| {
|
||||
// addr_of returns the pointer directly (the operand is already a ptr from alloca)
|
||||
self.mapRef(self.resolveRef(un.operand));
|
||||
},
|
||||
.deref => |un| {
|
||||
const ptr = self.resolveRef(un.operand);
|
||||
const llvm_ty = self.toLLVMType(instruction.ty);
|
||||
self.mapRef(c.LLVMBuildLoad2(self.builder, llvm_ty, ptr, "deref"));
|
||||
},
|
||||
.addr_of => |un| self.ops().emitAddrOf(un),
|
||||
.deref => |un| self.ops().emitDeref(instruction, un),
|
||||
|
||||
// ── Calls ─────────────────────────────────────────────
|
||||
.objc_msg_send => |msg| {
|
||||
@@ -3273,7 +3147,7 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
// ── Conversion helpers ──────────────────────────────────────────
|
||||
|
||||
fn emitConversion(self: *LLVMEmitter, operand: c.LLVMValueRef, from: TypeId, to: TypeId, to_ty: c.LLVMTypeRef) c.LLVMValueRef {
|
||||
pub fn emitConversion(self: *LLVMEmitter, operand: c.LLVMValueRef, from: TypeId, to: TypeId, to_ty: c.LLVMTypeRef) c.LLVMValueRef {
|
||||
const from_float = isFloatOrVecFloat(from, &self.ir_mod.types);
|
||||
const to_float = isFloatOrVecFloat(to, &self.ir_mod.types);
|
||||
|
||||
@@ -3588,7 +3462,7 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
/// Coerce a call argument to match the expected parameter type.
|
||||
/// Handles int width mismatches (trunc/ext), float width, and int↔float.
|
||||
fn coerceArg(self: *LLVMEmitter, val: c.LLVMValueRef, param_ty: c.LLVMTypeRef) c.LLVMValueRef {
|
||||
pub fn coerceArg(self: *LLVMEmitter, val: c.LLVMValueRef, param_ty: c.LLVMTypeRef) c.LLVMValueRef {
|
||||
const val_ty = c.LLVMTypeOf(val);
|
||||
if (val_ty == param_ty) return val;
|
||||
const val_kind = c.LLVMGetTypeKind(val_ty);
|
||||
@@ -3714,7 +3588,7 @@ pub const LLVMEmitter = struct {
|
||||
}
|
||||
|
||||
/// Look up the IR type of a Ref in the current function (for store coercion).
|
||||
fn getRefIRType(self: *LLVMEmitter, ref: Ref) ?TypeId {
|
||||
pub fn getRefIRType(self: *LLVMEmitter, ref: Ref) ?TypeId {
|
||||
const func = &self.ir_mod.functions.items[self.current_func_idx];
|
||||
const idx = ref.index();
|
||||
// Check if it's a function param (refs 0..N-1)
|
||||
|
||||
Reference in New Issue
Block a user