refactor(backend): extract scalar instruction handlers into ops.zig (A7.4 slice a)
Move the Constants/Arithmetic/Bitwise/Comparisons/Logical opcode handler bodies out of emitInst into a new Ops facade in src/backend/llvm/ops.zig. emitInst's scalar arms now delegate via self.ops().*; the shared infra they call (mapRef/resolveRef/matchBinOpTypes/emitCmp/emitCmpOrdered/emitStrCmp/ emitStringConstant/reflection + isFloatOrVecFloat/isSignedType) stays on LLVMEmitter, widened to pub as needed. Pure relocation: zero snapshot churn.
This commit is contained in:
289
src/backend/llvm/ops.zig
Normal file
289
src/backend/llvm/ops.zig
Normal file
@@ -0,0 +1,289 @@
|
||||
const std = @import("std");
|
||||
const llvm = @import("../../llvm_api.zig");
|
||||
const c = llvm.c;
|
||||
const emit = @import("../../ir/emit_llvm.zig");
|
||||
const ir_inst = @import("../../ir/inst.zig");
|
||||
const ir_types = @import("../../ir/types.zig");
|
||||
|
||||
const LLVMEmitter = emit.LLVMEmitter;
|
||||
const Inst = ir_inst.Inst;
|
||||
const BinOp = ir_inst.BinOp;
|
||||
const UnaryOp = ir_inst.UnaryOp;
|
||||
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
|
||||
/// (`mapRef`/`resolveRef`/`matchBinOpTypes`/`emitCmp`/`emitCmpOrdered`/
|
||||
/// `emitStrCmp`/`emitStringConstant`/`reflection`) stays on `LLVMEmitter`.
|
||||
/// `emitInst`'s scalar arms reach these via `self.ops()`.
|
||||
pub const Ops = struct {
|
||||
e: *LLVMEmitter,
|
||||
|
||||
// ── Constants ───────────────────────────────────────────
|
||||
pub fn emitConstInt(self: Ops, instruction: *const Inst, val: i64) void {
|
||||
const ty = self.e.toLLVMType(instruction.ty);
|
||||
const kind = c.LLVMGetTypeKind(ty);
|
||||
const llvm_val = if (kind == c.LLVMIntegerTypeKind)
|
||||
c.LLVMConstInt(ty, @bitCast(val), 1)
|
||||
else if (kind == c.LLVMPointerTypeKind)
|
||||
c.LLVMConstNull(ty)
|
||||
else
|
||||
// void or other non-integer type: emit i64 0 as unused placeholder
|
||||
c.LLVMConstInt(c.LLVMInt64TypeInContext(self.e.context), 0, 0);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
|
||||
pub fn emitConstFloat(self: Ops, instruction: *const Inst, val: f64) void {
|
||||
const ty = self.e.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMConstReal(ty, val);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
|
||||
pub fn emitConstBool(self: Ops, val: bool) void {
|
||||
const llvm_val = c.LLVMConstInt(self.e.cached_i1, @intFromBool(val), 0);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
|
||||
pub fn emitIsComptime(self: Ops) void {
|
||||
// Compiled code is never the comptime interpreter → constant
|
||||
// `false`. A `if is_comptime() { … }` branch becomes dead.
|
||||
self.e.mapRef(c.LLVMConstInt(self.e.cached_i1, 0, 0));
|
||||
}
|
||||
|
||||
pub fn emitInterpPrintFrames(self: Ops) void {
|
||||
// No interpreter stack in compiled code; this only ever sits in
|
||||
// a dead `is_comptime()` branch. Emit nothing.
|
||||
self.e.advanceRefCounter();
|
||||
}
|
||||
|
||||
pub fn emitTraceFrame(self: Ops, instruction: *const Inst) void {
|
||||
self.e.mapRef(self.e.reflection().emitTraceFrame(instruction));
|
||||
}
|
||||
|
||||
pub fn emitTraceResolve(self: Ops, u: UnaryOp) void {
|
||||
// The operand is a `Frame*` stamped in by `.trace_frame` (as
|
||||
// i64); reinterpret and load it.
|
||||
const raw = self.e.resolveRef(u.operand);
|
||||
const frame_ty = self.e.getFrameStructType();
|
||||
const ptr = c.LLVMBuildIntToPtr(self.e.builder, raw, self.e.cached_ptr, "frame.ptr");
|
||||
self.e.mapRef(c.LLVMBuildLoad2(self.e.builder, frame_ty, ptr, "frame.val"));
|
||||
}
|
||||
|
||||
pub fn emitConstString(self: Ops, str_id: StringId) void {
|
||||
const str = self.e.ir_mod.types.getString(str_id);
|
||||
const llvm_val = self.e.emitStringConstant(str);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
|
||||
pub fn emitConstNull(self: Ops, instruction: *const Inst) void {
|
||||
const ty = if (instruction.ty == .void) self.e.cached_ptr else self.e.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMConstNull(ty);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
|
||||
pub fn emitConstUndef(self: Ops, instruction: *const Inst) void {
|
||||
if (instruction.ty == .void) {
|
||||
// void has no value — map to undef i64 as placeholder
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.cached_i64));
|
||||
} else {
|
||||
const ty = self.e.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMGetUndef(ty);
|
||||
self.e.mapRef(llvm_val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitConstType(self: Ops, tid: TypeId) void {
|
||||
// Type values are Any-shaped pairs:
|
||||
// { tag = .any.index() (the meta-marker),
|
||||
// value = tid.index() }
|
||||
// Lets storage in Any slots, struct fields,
|
||||
// `Type`-typed vars, and slice elements all round-
|
||||
// trip through the standard Any infrastructure.
|
||||
// `case type:` in `any_to_string` matches on
|
||||
// tag == `.any.index()`. Runtime `type_name(t)`
|
||||
// extracts the value field and indexes into the
|
||||
// type-name lookup table.
|
||||
const any_ty = self.e.getAnyStructType();
|
||||
const tag = c.LLVMConstInt(self.e.cached_i64, TypeId.any.index(), 0);
|
||||
const val = c.LLVMConstInt(self.e.cached_i64, tid.index(), 0);
|
||||
var result = c.LLVMGetUndef(any_ty);
|
||||
result = c.LLVMBuildInsertValue(self.e.builder, result, tag, 0, "ct.tag");
|
||||
result = c.LLVMBuildInsertValue(self.e.builder, result, val, 1, "ct.val");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
// ── Arithmetic ─────────────────────────────────────────
|
||||
pub fn emitAdd(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFAdd(self.e.builder, lhs, rhs, "fadd")
|
||||
else
|
||||
c.LLVMBuildAdd(self.e.builder, lhs, rhs, "add");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitSub(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFSub(self.e.builder, lhs, rhs, "fsub")
|
||||
else
|
||||
c.LLVMBuildSub(self.e.builder, lhs, rhs, "sub");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitMul(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFMul(self.e.builder, lhs, rhs, "fmul")
|
||||
else
|
||||
c.LLVMBuildMul(self.e.builder, lhs, rhs, "mul");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitDiv(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFDiv(self.e.builder, lhs, rhs, "fdiv")
|
||||
else if (emit.isSignedType(instruction.ty))
|
||||
c.LLVMBuildSDiv(self.e.builder, lhs, rhs, "sdiv")
|
||||
else
|
||||
c.LLVMBuildUDiv(self.e.builder, lhs, rhs, "udiv");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitMod(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFRem(self.e.builder, lhs, rhs, "fmod")
|
||||
else if (emit.isSignedType(instruction.ty))
|
||||
c.LLVMBuildSRem(self.e.builder, lhs, rhs, "srem")
|
||||
else
|
||||
c.LLVMBuildURem(self.e.builder, lhs, rhs, "urem");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
pub fn emitNeg(self: Ops, instruction: *const Inst, un: UnaryOp) void {
|
||||
const operand = self.e.resolveRef(un.operand);
|
||||
const is_float = emit.isFloatOrVecFloat(instruction.ty, &self.e.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFNeg(self.e.builder, operand, "fneg")
|
||||
else
|
||||
c.LLVMBuildNeg(self.e.builder, operand, "neg");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
// ── Bitwise ────────────────────────────────────────────
|
||||
pub fn emitBitAnd(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildAnd(self.e.builder, lhs, rhs, "and"));
|
||||
}
|
||||
|
||||
pub fn emitBitOr(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildOr(self.e.builder, lhs, rhs, "or"));
|
||||
}
|
||||
|
||||
pub fn emitBitXor(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildXor(self.e.builder, lhs, rhs, "xor"));
|
||||
}
|
||||
|
||||
pub fn emitBitNot(self: Ops, un: UnaryOp) void {
|
||||
const operand = self.e.resolveRef(un.operand);
|
||||
self.e.mapRef(c.LLVMBuildNot(self.e.builder, operand, "not"));
|
||||
}
|
||||
|
||||
pub fn emitShl(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.e.mapRef(c.LLVMBuildShl(self.e.builder, lhs, rhs, "shl"));
|
||||
}
|
||||
|
||||
pub fn emitShr(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
var lhs = self.e.resolveRef(bin.lhs);
|
||||
var rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
// Use arithmetic shift right for signed, logical for unsigned
|
||||
const result = if (emit.isSignedType(instruction.ty))
|
||||
c.LLVMBuildAShr(self.e.builder, lhs, rhs, "ashr")
|
||||
else
|
||||
c.LLVMBuildLShr(self.e.builder, lhs, rhs, "lshr");
|
||||
self.e.mapRef(result);
|
||||
}
|
||||
|
||||
// ── Comparisons ───────────────────────────────────────
|
||||
pub fn emitCmpEq(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmp(bin, instruction.ty, c.LLVMIntEQ, c.LLVMRealOEQ);
|
||||
}
|
||||
|
||||
pub fn emitCmpNe(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmp(bin, instruction.ty, c.LLVMIntNE, c.LLVMRealONE);
|
||||
}
|
||||
|
||||
pub fn emitCmpLt(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSLT, c.LLVMIntULT, c.LLVMRealOLT);
|
||||
}
|
||||
|
||||
pub fn emitCmpLe(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSLE, c.LLVMIntULE, c.LLVMRealOLE);
|
||||
}
|
||||
|
||||
pub fn emitCmpGt(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSGT, c.LLVMIntUGT, c.LLVMRealOGT);
|
||||
}
|
||||
|
||||
pub fn emitCmpGe(self: Ops, instruction: *const Inst, bin: BinOp) void {
|
||||
self.e.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSGE, c.LLVMIntUGE, c.LLVMRealOGE);
|
||||
}
|
||||
|
||||
pub fn emitStrEq(self: Ops, bin: BinOp) void {
|
||||
self.e.emitStrCmp(bin, true);
|
||||
}
|
||||
|
||||
pub fn emitStrNe(self: Ops, bin: BinOp) void {
|
||||
self.e.emitStrCmp(bin, false);
|
||||
}
|
||||
|
||||
// ── Logical ───────────────────────────────────────────
|
||||
pub fn emitBoolAnd(self: Ops, bin: BinOp) void {
|
||||
const lhs = self.e.resolveRef(bin.lhs);
|
||||
const rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.mapRef(c.LLVMBuildAnd(self.e.builder, lhs, rhs, "land"));
|
||||
}
|
||||
|
||||
pub fn emitBoolOr(self: Ops, bin: BinOp) void {
|
||||
const lhs = self.e.resolveRef(bin.lhs);
|
||||
const rhs = self.e.resolveRef(bin.rhs);
|
||||
self.e.mapRef(c.LLVMBuildOr(self.e.builder, lhs, rhs, "lor"));
|
||||
}
|
||||
|
||||
pub fn emitBoolNot(self: Ops, un: UnaryOp) void {
|
||||
const operand = self.e.resolveRef(un.operand);
|
||||
self.e.mapRef(c.LLVMBuildNot(self.e.builder, operand, "lnot"));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user