aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig76
-rw-r--r--src/codegen/llvm.zig188
-rw-r--r--src/codegen/llvm/bindings.zig6
3 files changed, 169 insertions, 101 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 1afa81b70f..95ce95f2e5 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -883,25 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
// TODO use a different strategy for add that communicates to the optimizer
// that wrapping is UB.
- .add, .ptr_add => try airBinOp( f, inst, " + "),
- .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
- .addsat => return f.fail("TODO: C backend: implement codegen for addsat", .{}),
+ .add, .ptr_add => try airBinOp (f, inst, " + "),
// TODO use a different strategy for sub that communicates to the optimizer
// that wrapping is UB.
- .sub, .ptr_sub => try airBinOp( f, inst, " - "),
- .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
- .subsat => return f.fail("TODO: C backend: implement codegen for subsat", .{}),
+ .sub, .ptr_sub => try airBinOp (f, inst, " - "),
// TODO use a different strategy for mul that communicates to the optimizer
// that wrapping is UB.
- .mul => try airBinOp( f, inst, " * "),
- .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
- .mulsat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}),
+ .mul => try airBinOp (f, inst, " * "),
// TODO use a different strategy for div that communicates to the optimizer
// that wrapping is UB.
.div => try airBinOp( f, inst, " / "),
.rem => try airBinOp( f, inst, " % "),
- // TODO implement modulus division
- .mod => try airBinOp( f, inst, " mod "),
+ .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
+
+ .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
+ .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
+ .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
+
+ .add_sat => try airSatOp(f, inst, "adds_"),
+ .sub_sat => try airSatOp(f, inst, "subs_"),
+ .mul_sat => try airSatOp(f, inst, "muls_"),
+ .shl_sat => try airSatOp(f, inst, "shls_"),
.cmp_eq => try airBinOp(f, inst, " == "),
.cmp_gt => try airBinOp(f, inst, " > "),
@@ -911,18 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.cmp_neq => try airBinOp(f, inst, " != "),
// bool_and and bool_or are non-short-circuit operations
- .bool_and => try airBinOp(f, inst, " & "),
- .bool_or => try airBinOp(f, inst, " | "),
- .bit_and => try airBinOp(f, inst, " & "),
- .bit_or => try airBinOp(f, inst, " | "),
- .xor => try airBinOp(f, inst, " ^ "),
-
- .shr => try airBinOp(f, inst, " >> "),
- .shl => try airBinOp(f, inst, " << "),
- .shl_sat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}),
-
-
- .not => try airNot( f, inst),
+ .bool_and => try airBinOp(f, inst, " & "),
+ .bool_or => try airBinOp(f, inst, " | "),
+ .bit_and => try airBinOp(f, inst, " & "),
+ .bit_or => try airBinOp(f, inst, " | "),
+ .xor => try airBinOp(f, inst, " ^ "),
+ .shr => try airBinOp(f, inst, " >> "),
+ .shl, .shl_exact => try airBinOp(f, inst, " << "),
+ .not => try airNot (f, inst),
.optional_payload => try airOptionalPayload(f, inst),
.optional_payload_ptr => try airOptionalPayload(f, inst),
@@ -1314,27 +1312,23 @@ fn airWrapOp(
return ret;
}
-fn airSatOp(
- o: *Object,
- inst: Air.Inst.Index,
- fn_op: [*:0]const u8,
-) !CValue {
- if (o.liveness.isUnused(inst))
+fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const bin_op = o.air.instructions.items(.data)[inst].bin_op;
- const inst_ty = o.air.typeOfIndex(inst);
- const int_info = inst_ty.intInfo(o.dg.module.getTarget());
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
const bits = int_info.bits;
switch (bits) {
8, 16, 32, 64, 128 => {},
- else => return o.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),
+ else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),
}
// if it's an unsigned int with non-arbitrary bit size then we can just add
if (bits > 64) {
- return o.dg.fail("TODO: C backend: airSatOp for large integers", .{});
+ return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{});
}
var min_buf: [80]u8 = undefined;
@@ -1382,11 +1376,11 @@ fn airSatOp(
},
};
- const lhs = try o.resolveInst(bin_op.lhs);
- const rhs = try o.resolveInst(bin_op.rhs);
- const w = o.writer();
+ const lhs = try f.resolveInst(bin_op.lhs);
+ const rhs = try f.resolveInst(bin_op.rhs);
+ const w = f.object.writer();
- const ret = try o.allocLocal(inst_ty, .Mut);
+ const ret = try f.allocLocal(inst_ty, .Mut);
try w.print(" = zig_{s}", .{fn_op});
switch (inst_ty.tag()) {
@@ -1412,16 +1406,16 @@ fn airSatOp(
}
try w.writeByte('(');
- try o.writeCValue(w, lhs);
+ try f.writeCValue(w, lhs);
try w.writeAll(", ");
- try o.writeCValue(w, rhs);
+ try f.writeCValue(w, rhs);
if (int_info.signedness == .signed) {
try w.print(", {s}", .{min});
}
try w.print(", {s});", .{max});
- try o.indent_writer.insertNewline();
+ try f.object.indent_writer.insertNewline();
return ret;
}
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index cdd19146b5..b27afa9b54 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1236,27 +1236,27 @@ pub const FuncGen = struct {
for (body) |inst| {
const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
// zig fmt: off
- .add => try self.airAdd(inst, .standard),
- .addwrap => try self.airAdd(inst, .wrapping),
- .addsat => try self.airAdd(inst, .saturated),
- .sub => try self.airSub(inst, .standard),
- .subwrap => try self.airSub(inst, .wrapping),
- .subsat => try self.airSub(inst, .saturated),
- .mul => try self.airMul(inst, .standard),
- .mulwrap => try self.airMul(inst, .wrapping),
- .mulsat => try self.airMul(inst, .saturated),
- .div => try self.airDiv(inst),
- .rem => try self.airRem(inst),
- .mod => try self.airMod(inst),
- .ptr_add => try self.airPtrAdd(inst),
- .ptr_sub => try self.airPtrSub(inst),
+ .add => try self.airAdd(inst),
+ .addwrap => try self.airAddWrap(inst),
+ .add_sat => try self.airAddSat(inst),
+ .sub => try self.airSub(inst),
+ .subwrap => try self.airSubWrap(inst),
+ .sub_sat => try self.airSubSat(inst),
+ .mul => try self.airMul(inst),
+ .mulwrap => try self.airMulWrap(inst),
+ .mul_sat => try self.airMulSat(inst),
+ .div => try self.airDiv(inst),
+ .rem => try self.airRem(inst),
+ .mod => try self.airMod(inst),
+ .ptr_add => try self.airPtrAdd(inst),
+ .ptr_sub => try self.airPtrSub(inst),
+ .shl => try self.airShl(inst),
+ .shl_sat => try self.airShlSat(inst),
+ .shl_exact => try self.airShlExact(inst),
.bit_and, .bool_and => try self.airAnd(inst),
.bit_or, .bool_or => try self.airOr(inst),
.xor => try self.airXor(inst),
-
- .shl => try self.airShl(inst, false),
- .shl_sat => try self.airShl(inst, true),
.shr => try self.airShr(inst),
.cmp_eq => try self.airCmp(inst, .eq),
@@ -2028,10 +2028,8 @@ pub const FuncGen = struct {
return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
}
- const ArithmeticType = enum { standard, wrapping, saturated };
- fn airAdd(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
- if (self.liveness.isUnused(inst))
- return null;
+ fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@@ -2039,21 +2037,36 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
- if (ty == .wrapping)
- return self.builder.buildAdd(lhs, rhs, "")
- else if (ty == .saturated) {
- if (inst_ty.isSignedInt())
- return self.builder.buildSAddSat(lhs, rhs, "")
- else
- return self.builder.buildUAddSat(lhs, rhs, "");
- }
if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
return self.builder.buildNUWAdd(lhs, rhs, "");
}
- fn airSub(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
- if (self.liveness.isUnused(inst))
- return null;
+ fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+
+ return self.builder.buildAdd(lhs, rhs, "");
+ }
+
+ fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+
+ if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{});
+ if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, "");
+
+ return self.builder.buildUAddSat(lhs, rhs, "");
+ }
+
+ fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@@ -2061,21 +2074,35 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
- if (ty == .wrapping)
- return self.builder.buildSub(lhs, rhs, "")
- else if (ty == .saturated) {
- if (inst_ty.isSignedInt())
- return self.builder.buildSSubSat(lhs, rhs, "")
- else
- return self.builder.buildUSubSat(lhs, rhs, "");
- }
if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
return self.builder.buildNUWSub(lhs, rhs, "");
}
- fn airMul(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
- if (self.liveness.isUnused(inst))
- return null;
+ fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+
+ return self.builder.buildSub(lhs, rhs, "");
+ }
+
+ fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+
+ if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{});
+ if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, "");
+ return self.builder.buildUSubSat(lhs, rhs, "");
+ }
+
+ fn airMul(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@@ -2083,18 +2110,33 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
- if (ty == .wrapping)
- return self.builder.buildMul(lhs, rhs, "")
- else if (ty == .saturated) {
- if (inst_ty.isSignedInt())
- return self.builder.buildSMulFixSat(lhs, rhs, "")
- else
- return self.builder.buildUMulFixSat(lhs, rhs, "");
- }
if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
return self.builder.buildNUWMul(lhs, rhs, "");
}
+ fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+
+ return self.builder.buildMul(lhs, rhs, "");
+ }
+
+ fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+
+ if (inst_ty.isAnyFloat()) return self.todo("saturating float mul", .{});
+ if (inst_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, "");
+ return self.builder.buildUMulFixSat(lhs, rhs, "");
+ }
+
fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
@@ -2200,9 +2242,25 @@ pub const FuncGen = struct {
return self.builder.buildXor(lhs, rhs, "");
}
- fn airShl(self: *FuncGen, inst: Air.Inst.Index, sat: bool) !?*const llvm.Value {
- if (self.liveness.isUnused(inst))
- return null;
+ fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const lhs_type = self.air.typeOf(bin_op.lhs);
+ const tg = self.dg.module.getTarget();
+ const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
+ self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
+ else
+ rhs;
+ if (lhs_type.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, "");
+ return self.builder.buildNUWShl(lhs, casted_rhs, "");
+ }
+
+ fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@@ -2212,15 +2270,25 @@ pub const FuncGen = struct {
self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
else
rhs;
- if (sat) {
- return if (lhs_type.isSignedInt())
- self.builder.buildSShlSat(lhs, casted_rhs, "")
- else
- self.builder.buildUShlSat(lhs, casted_rhs, "");
- }
return self.builder.buildShl(lhs, casted_rhs, "");
}
+ fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const lhs_type = self.air.typeOf(bin_op.lhs);
+ const tg = self.dg.module.getTarget();
+ const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
+ self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
+ else
+ rhs;
+ if (lhs_type.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, "");
+ return self.builder.buildUShlSat(lhs, casted_rhs, "");
+ }
+
fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index 178c381235..4fac6656c8 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -469,6 +469,12 @@ pub const Builder = opaque {
pub const buildShl = LLVMBuildShl;
extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildNUWShl = ZigLLVMBuildNUWShl;
+ extern fn ZigLLVMBuildNUWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildNSWShl = ZigLLVMBuildNSWShl;
+ extern fn ZigLLVMBuildNSWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildSShlSat = ZigLLVMBuildSShlSat;
extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;