Merge branch 'flow/sx-plan-arch/A7.4a' into arch-refactor
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"));
|
||||
}
|
||||
};
|
||||
@@ -15,6 +15,7 @@ const llvm_abi = @import("../backend/llvm/abi.zig");
|
||||
const llvm_debug = @import("../backend/llvm/debug.zig");
|
||||
const llvm_reflection = @import("../backend/llvm/reflection.zig");
|
||||
const llvm_ffi_ctors = @import("../backend/llvm/ffi_ctors.zig");
|
||||
const llvm_ops = @import("../backend/llvm/ops.zig");
|
||||
const ir_inst = @import("inst.zig");
|
||||
const Ref = ir_inst.Ref;
|
||||
const Span = ir_inst.Span;
|
||||
@@ -1345,224 +1346,48 @@ pub const LLVMEmitter = struct {
|
||||
self.debugInfo().setInstDebugLocation(instruction.span);
|
||||
switch (instruction.op) {
|
||||
// ── Constants ───────────────────────────────────────────
|
||||
.const_int => |val| {
|
||||
const ty = self.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.context), 0, 0);
|
||||
self.mapRef(llvm_val);
|
||||
},
|
||||
.const_float => |val| {
|
||||
const ty = self.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMConstReal(ty, val);
|
||||
self.mapRef(llvm_val);
|
||||
},
|
||||
.const_bool => |val| {
|
||||
const llvm_val = c.LLVMConstInt(self.cached_i1, @intFromBool(val), 0);
|
||||
self.mapRef(llvm_val);
|
||||
},
|
||||
.is_comptime => {
|
||||
// Compiled code is never the comptime interpreter → constant
|
||||
// `false`. A `if is_comptime() { … }` branch becomes dead.
|
||||
self.mapRef(c.LLVMConstInt(self.cached_i1, 0, 0));
|
||||
},
|
||||
.interp_print_frames => {
|
||||
// No interpreter stack in compiled code; this only ever sits in
|
||||
// a dead `is_comptime()` branch. Emit nothing.
|
||||
self.advanceRefCounter();
|
||||
},
|
||||
.trace_frame => {
|
||||
self.mapRef(self.reflection().emitTraceFrame(instruction));
|
||||
},
|
||||
.trace_resolve => |u| {
|
||||
// The operand is a `Frame*` stamped in by `.trace_frame` (as
|
||||
// i64); reinterpret and load it.
|
||||
const raw = self.resolveRef(u.operand);
|
||||
const frame_ty = self.getFrameStructType();
|
||||
const ptr = c.LLVMBuildIntToPtr(self.builder, raw, self.cached_ptr, "frame.ptr");
|
||||
self.mapRef(c.LLVMBuildLoad2(self.builder, frame_ty, ptr, "frame.val"));
|
||||
},
|
||||
.const_string => |str_id| {
|
||||
const str = self.ir_mod.types.getString(str_id);
|
||||
const llvm_val = self.emitStringConstant(str);
|
||||
self.mapRef(llvm_val);
|
||||
},
|
||||
.const_null => {
|
||||
const ty = if (instruction.ty == .void) self.cached_ptr else self.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMConstNull(ty);
|
||||
self.mapRef(llvm_val);
|
||||
},
|
||||
.const_undef => {
|
||||
if (instruction.ty == .void) {
|
||||
// void has no value — map to undef i64 as placeholder
|
||||
self.mapRef(c.LLVMGetUndef(self.cached_i64));
|
||||
} else {
|
||||
const ty = self.toLLVMType(instruction.ty);
|
||||
const llvm_val = c.LLVMGetUndef(ty);
|
||||
self.mapRef(llvm_val);
|
||||
}
|
||||
},
|
||||
.const_type => |tid| {
|
||||
// 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.getAnyStructType();
|
||||
const tag = c.LLVMConstInt(self.cached_i64, TypeId.any.index(), 0);
|
||||
const val = c.LLVMConstInt(self.cached_i64, tid.index(), 0);
|
||||
var result = c.LLVMGetUndef(any_ty);
|
||||
result = c.LLVMBuildInsertValue(self.builder, result, tag, 0, "ct.tag");
|
||||
result = c.LLVMBuildInsertValue(self.builder, result, val, 1, "ct.val");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.const_int => |val| self.ops().emitConstInt(instruction, val),
|
||||
.const_float => |val| self.ops().emitConstFloat(instruction, val),
|
||||
.const_bool => |val| self.ops().emitConstBool(val),
|
||||
.is_comptime => self.ops().emitIsComptime(),
|
||||
.interp_print_frames => self.ops().emitInterpPrintFrames(),
|
||||
.trace_frame => self.ops().emitTraceFrame(instruction),
|
||||
.trace_resolve => |u| self.ops().emitTraceResolve(u),
|
||||
.const_string => |str_id| self.ops().emitConstString(str_id),
|
||||
.const_null => self.ops().emitConstNull(instruction),
|
||||
.const_undef => self.ops().emitConstUndef(instruction),
|
||||
.const_type => |tid| self.ops().emitConstType(tid),
|
||||
|
||||
// ── Arithmetic ─────────────────────────────────────────
|
||||
.add => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFAdd(self.builder, lhs, rhs, "fadd")
|
||||
else
|
||||
c.LLVMBuildAdd(self.builder, lhs, rhs, "add");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.sub => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFSub(self.builder, lhs, rhs, "fsub")
|
||||
else
|
||||
c.LLVMBuildSub(self.builder, lhs, rhs, "sub");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.mul => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFMul(self.builder, lhs, rhs, "fmul")
|
||||
else
|
||||
c.LLVMBuildMul(self.builder, lhs, rhs, "mul");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.div => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFDiv(self.builder, lhs, rhs, "fdiv")
|
||||
else if (isSignedType(instruction.ty))
|
||||
c.LLVMBuildSDiv(self.builder, lhs, rhs, "sdiv")
|
||||
else
|
||||
c.LLVMBuildUDiv(self.builder, lhs, rhs, "udiv");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.mod => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFRem(self.builder, lhs, rhs, "fmod")
|
||||
else if (isSignedType(instruction.ty))
|
||||
c.LLVMBuildSRem(self.builder, lhs, rhs, "srem")
|
||||
else
|
||||
c.LLVMBuildURem(self.builder, lhs, rhs, "urem");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.neg => |un| {
|
||||
const operand = self.resolveRef(un.operand);
|
||||
const is_float = isFloatOrVecFloat(instruction.ty, &self.ir_mod.types);
|
||||
const result = if (is_float)
|
||||
c.LLVMBuildFNeg(self.builder, operand, "fneg")
|
||||
else
|
||||
c.LLVMBuildNeg(self.builder, operand, "neg");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.add => |bin| self.ops().emitAdd(instruction, bin),
|
||||
.sub => |bin| self.ops().emitSub(instruction, bin),
|
||||
.mul => |bin| self.ops().emitMul(instruction, bin),
|
||||
.div => |bin| self.ops().emitDiv(instruction, bin),
|
||||
.mod => |bin| self.ops().emitMod(instruction, bin),
|
||||
.neg => |un| self.ops().emitNeg(instruction, un),
|
||||
|
||||
// ── Bitwise ────────────────────────────────────────────
|
||||
.bit_and => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.mapRef(c.LLVMBuildAnd(self.builder, lhs, rhs, "and"));
|
||||
},
|
||||
.bit_or => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.mapRef(c.LLVMBuildOr(self.builder, lhs, rhs, "or"));
|
||||
},
|
||||
.bit_xor => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.mapRef(c.LLVMBuildXor(self.builder, lhs, rhs, "xor"));
|
||||
},
|
||||
.bit_not => |un| {
|
||||
const operand = self.resolveRef(un.operand);
|
||||
self.mapRef(c.LLVMBuildNot(self.builder, operand, "not"));
|
||||
},
|
||||
.shl => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
self.mapRef(c.LLVMBuildShl(self.builder, lhs, rhs, "shl"));
|
||||
},
|
||||
.shr => |bin| {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
self.matchBinOpTypes(&lhs, &rhs, instruction.ty);
|
||||
// Use arithmetic shift right for signed, logical for unsigned
|
||||
const result = if (isSignedType(instruction.ty))
|
||||
c.LLVMBuildAShr(self.builder, lhs, rhs, "ashr")
|
||||
else
|
||||
c.LLVMBuildLShr(self.builder, lhs, rhs, "lshr");
|
||||
self.mapRef(result);
|
||||
},
|
||||
.bit_and => |bin| self.ops().emitBitAnd(instruction, bin),
|
||||
.bit_or => |bin| self.ops().emitBitOr(instruction, bin),
|
||||
.bit_xor => |bin| self.ops().emitBitXor(instruction, bin),
|
||||
.bit_not => |un| self.ops().emitBitNot(un),
|
||||
.shl => |bin| self.ops().emitShl(instruction, bin),
|
||||
.shr => |bin| self.ops().emitShr(instruction, bin),
|
||||
|
||||
// ── Comparisons ───────────────────────────────────────
|
||||
.cmp_eq => |bin| self.emitCmp(bin, instruction.ty, c.LLVMIntEQ, c.LLVMRealOEQ),
|
||||
.cmp_ne => |bin| self.emitCmp(bin, instruction.ty, c.LLVMIntNE, c.LLVMRealONE),
|
||||
.cmp_lt => |bin| self.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSLT, c.LLVMIntULT, c.LLVMRealOLT),
|
||||
.cmp_le => |bin| self.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSLE, c.LLVMIntULE, c.LLVMRealOLE),
|
||||
.cmp_gt => |bin| self.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSGT, c.LLVMIntUGT, c.LLVMRealOGT),
|
||||
.cmp_ge => |bin| self.emitCmpOrdered(bin, instruction.ty, c.LLVMIntSGE, c.LLVMIntUGE, c.LLVMRealOGE),
|
||||
.str_eq => |bin| self.emitStrCmp(bin, true),
|
||||
.str_ne => |bin| self.emitStrCmp(bin, false),
|
||||
.cmp_eq => |bin| self.ops().emitCmpEq(instruction, bin),
|
||||
.cmp_ne => |bin| self.ops().emitCmpNe(instruction, bin),
|
||||
.cmp_lt => |bin| self.ops().emitCmpLt(instruction, bin),
|
||||
.cmp_le => |bin| self.ops().emitCmpLe(instruction, bin),
|
||||
.cmp_gt => |bin| self.ops().emitCmpGt(instruction, bin),
|
||||
.cmp_ge => |bin| self.ops().emitCmpGe(instruction, bin),
|
||||
.str_eq => |bin| self.ops().emitStrEq(bin),
|
||||
.str_ne => |bin| self.ops().emitStrNe(bin),
|
||||
|
||||
// ── Logical ───────────────────────────────────────────
|
||||
.bool_and => |bin| {
|
||||
const lhs = self.resolveRef(bin.lhs);
|
||||
const rhs = self.resolveRef(bin.rhs);
|
||||
self.mapRef(c.LLVMBuildAnd(self.builder, lhs, rhs, "land"));
|
||||
},
|
||||
.bool_or => |bin| {
|
||||
const lhs = self.resolveRef(bin.lhs);
|
||||
const rhs = self.resolveRef(bin.rhs);
|
||||
self.mapRef(c.LLVMBuildOr(self.builder, lhs, rhs, "lor"));
|
||||
},
|
||||
.bool_not => |un| {
|
||||
const operand = self.resolveRef(un.operand);
|
||||
self.mapRef(c.LLVMBuildNot(self.builder, operand, "lnot"));
|
||||
},
|
||||
.bool_and => |bin| self.ops().emitBoolAnd(bin),
|
||||
.bool_or => |bin| self.ops().emitBoolOr(bin),
|
||||
.bool_not => |un| self.ops().emitBoolNot(un),
|
||||
|
||||
// ── Memory ────────────────────────────────────────────
|
||||
.alloca => |elem_ty| {
|
||||
@@ -3153,16 +2978,16 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
// ── Ref tracking ────────────────────────────────────────────────
|
||||
|
||||
fn mapRef(self: *LLVMEmitter, val: c.LLVMValueRef) void {
|
||||
pub fn mapRef(self: *LLVMEmitter, val: c.LLVMValueRef) void {
|
||||
self.ref_map.put(self.ref_counter, val) catch unreachable;
|
||||
self.ref_counter += 1;
|
||||
}
|
||||
|
||||
fn advanceRefCounter(self: *LLVMEmitter) void {
|
||||
pub fn advanceRefCounter(self: *LLVMEmitter) void {
|
||||
self.ref_counter += 1;
|
||||
}
|
||||
|
||||
fn resolveRef(self: *LLVMEmitter, ref: Ref) c.LLVMValueRef {
|
||||
pub fn resolveRef(self: *LLVMEmitter, ref: Ref) c.LLVMValueRef {
|
||||
if (ref.isNone()) {
|
||||
return c.LLVMGetUndef(self.cached_i64);
|
||||
}
|
||||
@@ -3276,7 +3101,7 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
// ── Comparison helpers ────────────────────────────────────────────
|
||||
|
||||
fn emitCmp(self: *LLVMEmitter, bin: ir_inst.BinOp, _: TypeId, int_pred: c_uint, float_pred: c_uint) void {
|
||||
pub fn emitCmp(self: *LLVMEmitter, bin: ir_inst.BinOp, _: TypeId, int_pred: c_uint, float_pred: c_uint) void {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
// Determine if float by inspecting operand LLVM type
|
||||
@@ -3358,7 +3183,7 @@ pub const LLVMEmitter = struct {
|
||||
self.mapRef(result);
|
||||
}
|
||||
|
||||
fn emitCmpOrdered(self: *LLVMEmitter, bin: ir_inst.BinOp, _: TypeId, signed_pred: c_uint, unsigned_pred: c_uint, float_pred: c_uint) void {
|
||||
pub fn emitCmpOrdered(self: *LLVMEmitter, bin: ir_inst.BinOp, _: TypeId, signed_pred: c_uint, unsigned_pred: c_uint, float_pred: c_uint) void {
|
||||
var lhs = self.resolveRef(bin.lhs);
|
||||
var rhs = self.resolveRef(bin.rhs);
|
||||
const lhs_ty = c.LLVMTypeOf(lhs);
|
||||
@@ -3391,7 +3216,7 @@ pub const LLVMEmitter = struct {
|
||||
}
|
||||
|
||||
/// String comparison via memcmp: compare length first, then content.
|
||||
fn emitStrCmp(self: *LLVMEmitter, bin: ir_inst.BinOp, is_eq: bool) void {
|
||||
pub fn emitStrCmp(self: *LLVMEmitter, bin: ir_inst.BinOp, is_eq: bool) void {
|
||||
const lhs = self.resolveRef(bin.lhs);
|
||||
const rhs = self.resolveRef(bin.rhs);
|
||||
const b = self.builder;
|
||||
@@ -3905,7 +3730,7 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
/// Coerce both binary operands to match the instruction's result type.
|
||||
/// E.g. if result is i64 but one operand is i32, sext it.
|
||||
fn matchBinOpTypes(self: *LLVMEmitter, lhs: *c.LLVMValueRef, rhs: *c.LLVMValueRef, result_ty: TypeId) void {
|
||||
pub fn matchBinOpTypes(self: *LLVMEmitter, lhs: *c.LLVMValueRef, rhs: *c.LLVMValueRef, result_ty: TypeId) void {
|
||||
const target = self.toLLVMType(result_ty);
|
||||
lhs.* = self.coerceArg(lhs.*, target);
|
||||
rhs.* = self.coerceArg(rhs.*, target);
|
||||
@@ -3925,7 +3750,7 @@ pub const LLVMEmitter = struct {
|
||||
return .{ .e = self };
|
||||
}
|
||||
|
||||
fn reflection(self: *LLVMEmitter) llvm_reflection.Reflection {
|
||||
pub fn reflection(self: *LLVMEmitter) llvm_reflection.Reflection {
|
||||
return .{ .e = self };
|
||||
}
|
||||
|
||||
@@ -3933,6 +3758,10 @@ pub const LLVMEmitter = struct {
|
||||
return .{ .e = self };
|
||||
}
|
||||
|
||||
fn ops(self: *LLVMEmitter) llvm_ops.Ops {
|
||||
return .{ .e = self };
|
||||
}
|
||||
|
||||
/// IR-type → LLVM-type lowering lives in `backend/llvm/types.zig`
|
||||
/// (`TypeLowering`). This stays the facade entry point (~97 callers).
|
||||
pub fn toLLVMType(self: *LLVMEmitter, ty: TypeId) c.LLVMTypeRef {
|
||||
@@ -4057,7 +3886,7 @@ pub const LLVMEmitter = struct {
|
||||
return c.LLVMConstArray(elem_ty, vals.ptr, n);
|
||||
}
|
||||
|
||||
fn emitStringConstant(self: *LLVMEmitter, str: []const u8) c.LLVMValueRef {
|
||||
pub fn emitStringConstant(self: *LLVMEmitter, str: []const u8) c.LLVMValueRef {
|
||||
// LLVMBuildGlobalStringPtr needs a null-terminated C string
|
||||
const str_z = self.alloc.dupeZ(u8, str) catch unreachable;
|
||||
defer self.alloc.free(str_z);
|
||||
@@ -4393,7 +4222,7 @@ fn isFloatType(ty: TypeId) bool {
|
||||
}
|
||||
|
||||
/// Check if a TypeId is a float type, including float vectors.
|
||||
fn isFloatOrVecFloat(ty: TypeId, types: *const TypeTable) bool {
|
||||
pub fn isFloatOrVecFloat(ty: TypeId, types: *const TypeTable) bool {
|
||||
if (ty == .f32 or ty == .f64) return true;
|
||||
if (!ty.isBuiltin()) {
|
||||
const info = types.get(ty);
|
||||
@@ -4402,7 +4231,7 @@ fn isFloatOrVecFloat(ty: TypeId, types: *const TypeTable) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isSignedType(ty: TypeId) bool {
|
||||
pub fn isSignedType(ty: TypeId) bool {
|
||||
return switch (ty) {
|
||||
.s8, .s16, .s32, .s64, .isize => true,
|
||||
else => false,
|
||||
|
||||
Reference in New Issue
Block a user