aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig147
-rw-r--r--src/codegen/llvm.zig160
-rw-r--r--src/codegen/llvm/bindings.zig30
3 files changed, 289 insertions, 48 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 4964f17cd3..95ce95f2e5 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -883,22 +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_"),
+ .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_"),
+ .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_"),
+ .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, " > "),
@@ -908,16 +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, " << "),
-
- .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),
@@ -1309,6 +1312,114 @@ fn airWrapOp(
return ret;
}
+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 = 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 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 f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{});
+ }
+
+ var min_buf: [80]u8 = undefined;
+ const min = switch (int_info.signedness) {
+ .unsigned => "0",
+ else => switch (inst_ty.tag()) {
+ .c_short => "SHRT_MIN",
+ .c_int => "INT_MIN",
+ .c_long => "LONG_MIN",
+ .c_longlong => "LLONG_MIN",
+ .isize => "INTPTR_MIN",
+ else => blk: {
+ // compute the type minimum based on the bitcount (bits)
+ const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1));
+ break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) {
+ error.NoSpaceLeft => unreachable,
+ else => |e| return e,
+ };
+ },
+ },
+ };
+
+ var max_buf: [80]u8 = undefined;
+ const max = switch (inst_ty.tag()) {
+ .c_short => "SHRT_MAX",
+ .c_ushort => "USHRT_MAX",
+ .c_int => "INT_MAX",
+ .c_uint => "UINT_MAX",
+ .c_long => "LONG_MAX",
+ .c_ulong => "ULONG_MAX",
+ .c_longlong => "LLONG_MAX",
+ .c_ulonglong => "ULLONG_MAX",
+ .isize => "INTPTR_MAX",
+ .usize => "UINTPTR_MAX",
+ else => blk: {
+ const pow_bits = switch (int_info.signedness) {
+ .signed => bits - 1,
+ .unsigned => bits,
+ };
+ const val = std.math.pow(u65, 2, pow_bits) - 1;
+ break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) {
+ error.NoSpaceLeft => unreachable,
+ else => |e| return e,
+ };
+ },
+ };
+
+ const lhs = try f.resolveInst(bin_op.lhs);
+ const rhs = try f.resolveInst(bin_op.rhs);
+ const w = f.object.writer();
+
+ const ret = try f.allocLocal(inst_ty, .Mut);
+ try w.print(" = zig_{s}", .{fn_op});
+
+ switch (inst_ty.tag()) {
+ .isize => try w.writeAll("isize"),
+ .c_short => try w.writeAll("short"),
+ .c_int => try w.writeAll("int"),
+ .c_long => try w.writeAll("long"),
+ .c_longlong => try w.writeAll("longlong"),
+ else => {
+ const prefix_byte: u8 = switch (int_info.signedness) {
+ .signed => 'i',
+ .unsigned => 'u',
+ };
+ for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
+ if (bits <= nbits) {
+ try w.print("{c}{d}", .{ prefix_byte, nbits });
+ break;
+ }
+ } else {
+ unreachable;
+ }
+ },
+ }
+
+ try w.writeByte('(');
+ try f.writeCValue(w, lhs);
+ try w.writeAll(", ");
+ try f.writeCValue(w, rhs);
+
+ if (int_info.signedness == .signed) {
+ try w.print(", {s}", .{min});
+ }
+
+ try w.print(", {s});", .{max});
+ try f.object.indent_writer.insertNewline();
+
+ return ret;
+}
+
fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index f65e0f6d72..b27afa9b54 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1236,23 +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, false),
- .addwrap => try self.airAdd(inst, true),
- .sub => try self.airSub(inst, false),
- .subwrap => try self.airSub(inst, true),
- .mul => try self.airMul(inst, false),
- .mulwrap => try self.airMul(inst, true),
- .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),
.shr => try self.airShr(inst),
.cmp_eq => try self.airCmp(inst, .eq),
@@ -2024,51 +2028,115 @@ pub const FuncGen = struct {
return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
}
- fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*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);
const rhs = try self.resolveInst(bin_op.rhs);
const inst_ty = self.air.typeOfIndex(inst);
- if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
- if (wrap) return self.builder.buildAdd(lhs, rhs, "");
+ if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(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, wrap: bool) !?*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);
const rhs = try self.resolveInst(bin_op.rhs);
const inst_ty = self.air.typeOfIndex(inst);
- if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
- if (wrap) return self.builder.buildSub(lhs, rhs, "");
+ if (inst_ty.isAnyFloat()) return self.builder.buildFSub(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, wrap: bool) !?*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.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
- if (wrap) return self.builder.buildMul(lhs, rhs, "");
+ 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);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+
+ if (inst_ty.isAnyFloat()) return self.builder.buildFMul(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;
@@ -2174,9 +2242,25 @@ pub const FuncGen = struct {
return self.builder.buildXor(lhs, rhs, "");
}
+ 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;
+ 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);
@@ -2189,6 +2273,22 @@ pub const FuncGen = struct {
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 9d32682260..4fac6656c8 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -397,6 +397,12 @@ pub const Builder = opaque {
pub const buildNUWAdd = LLVMBuildNUWAdd;
extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildSAddSat = ZigLLVMBuildSAddSat;
+ extern fn ZigLLVMBuildSAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildUAddSat = ZigLLVMBuildUAddSat;
+ extern fn ZigLLVMBuildUAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildFSub = LLVMBuildFSub;
extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@@ -409,6 +415,12 @@ pub const Builder = opaque {
pub const buildNUWSub = LLVMBuildNUWSub;
extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildSSubSat = ZigLLVMBuildSSubSat;
+ extern fn ZigLLVMBuildSSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildUSubSat = ZigLLVMBuildUSubSat;
+ extern fn ZigLLVMBuildUSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildFMul = LLVMBuildFMul;
extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@@ -421,6 +433,12 @@ pub const Builder = opaque {
pub const buildNUWMul = LLVMBuildNUWMul;
extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildSMulFixSat = ZigLLVMBuildSMulFixSat;
+ extern fn ZigLLVMBuildSMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildUMulFixSat = ZigLLVMBuildUMulFixSat;
+ extern fn ZigLLVMBuildUMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildUDiv = LLVMBuildUDiv;
extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@@ -451,6 +469,18 @@ 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;
+
+ pub const buildUShlSat = ZigLLVMBuildUShlSat;
+ extern fn ZigLLVMBuildUShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildOr = LLVMBuildOr;
extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;