aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Air.zig11
-rw-r--r--src/Liveness.zig1
-rw-r--r--src/Module.zig238
-rw-r--r--src/Sema.zig104
-rw-r--r--src/codegen.zig14
-rw-r--r--src/codegen/c.zig15
-rw-r--r--src/codegen/llvm.zig11
-rw-r--r--src/codegen/llvm/bindings.zig8
-rw-r--r--src/print_air.zig1
-rw-r--r--src/value.zig238
-rw-r--r--test/behavior/basic.zig11
-rw-r--r--test/behavior/misc.zig7
12 files changed, 380 insertions, 279 deletions
diff --git a/src/Air.zig b/src/Air.zig
index d202c079bc..d923bf0b02 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -206,10 +206,16 @@ pub const Inst = struct {
/// Convert from one float type to another.
/// Uses the `ty_op` field.
floatcast,
- /// TODO audit uses of this. We should have explicit instructions for integer
- /// widening and truncating.
+ /// Returns an integer with a different type than the operand. The new type may have
+ /// fewer, the same, or more bits than the operand type. However, the instruction
+ /// guarantees that the same integer value fits in both types.
+ /// See `trunc` for integer truncation.
/// Uses the `ty_op` field.
intcast,
+ /// Truncate higher bits from an integer, resulting in an integer with the same
+ /// sign but an equal or smaller number of bits.
+ /// Uses the `ty_op` field.
+ trunc,
/// ?T => T. If the value is null, undefined behavior.
/// Uses the `ty_op` field.
optional_payload,
@@ -452,6 +458,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.load,
.floatcast,
.intcast,
+ .trunc,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
diff --git a/src/Liveness.zig b/src/Liveness.zig
index 7ba062fa31..4e22febc5a 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -264,6 +264,7 @@ fn analyzeInst(
.load,
.floatcast,
.intcast,
+ .trunc,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
diff --git a/src/Module.zig b/src/Module.zig
index d87f20621c..84b721369d 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -4033,244 +4033,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co
return error.AnalysisFail;
}
-pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.add(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- if (result_bigint.positive) {
- return Value.Tag.int_big_positive.create(allocator, result_limbs);
- } else {
- return Value.Tag.int_big_negative.create(allocator, result_limbs);
- }
-}
-
-pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.sub(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- if (result_bigint.positive) {
- return Value.Tag.int_big_positive.create(allocator, result_limbs);
- } else {
- return Value.Tag.int_big_negative.create(allocator, result_limbs);
- }
-}
-
-pub fn intDiv(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs_q = try allocator.alloc(
- std.math.big.Limb,
- lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
- );
- const limbs_r = try allocator.alloc(
- std.math.big.Limb,
- lhs_bigint.limbs.len,
- );
- const limbs_buffer = try allocator.alloc(
- std.math.big.Limb,
- std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
- );
- var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
- var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
- result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
- const result_limbs = result_q.limbs[0..result_q.len];
-
- if (result_q.positive) {
- return Value.Tag.int_big_positive.create(allocator, result_limbs);
- } else {
- return Value.Tag.int_big_negative.create(allocator, result_limbs);
- }
-}
-
-pub fn intMul(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- var limbs_buffer = try allocator.alloc(
- std.math.big.Limb,
- std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
- );
- defer allocator.free(limbs_buffer);
- result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- if (result_bigint.positive) {
- return Value.Tag.int_big_positive.create(allocator, result_limbs);
- } else {
- return Value.Tag.int_big_negative.create(allocator, result_limbs);
- }
-}
-
-pub fn floatAdd(
- arena: *Allocator,
- float_type: Type,
- src: LazySrcLoc,
- lhs: Value,
- rhs: Value,
-) !Value {
- _ = src;
- switch (float_type.tag()) {
- .f16 => {
- @panic("TODO add __trunctfhf2 to compiler-rt");
- //const lhs_val = lhs.toFloat(f16);
- //const rhs_val = rhs.toFloat(f16);
- //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
- },
- .f32 => {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
- },
- .f64 => {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
- },
- .f128, .comptime_float, .c_longdouble => {
- const lhs_val = lhs.toFloat(f128);
- const rhs_val = rhs.toFloat(f128);
- return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
- },
- else => unreachable,
- }
-}
-
-pub fn floatSub(
- arena: *Allocator,
- float_type: Type,
- src: LazySrcLoc,
- lhs: Value,
- rhs: Value,
-) !Value {
- _ = src;
- switch (float_type.tag()) {
- .f16 => {
- @panic("TODO add __trunctfhf2 to compiler-rt");
- //const lhs_val = lhs.toFloat(f16);
- //const rhs_val = rhs.toFloat(f16);
- //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
- },
- .f32 => {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
- },
- .f64 => {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
- },
- .f128, .comptime_float, .c_longdouble => {
- const lhs_val = lhs.toFloat(f128);
- const rhs_val = rhs.toFloat(f128);
- return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
- },
- else => unreachable,
- }
-}
-
-pub fn floatDiv(
- arena: *Allocator,
- float_type: Type,
- src: LazySrcLoc,
- lhs: Value,
- rhs: Value,
-) !Value {
- _ = src;
- switch (float_type.tag()) {
- .f16 => {
- @panic("TODO add __trunctfhf2 to compiler-rt");
- //const lhs_val = lhs.toFloat(f16);
- //const rhs_val = rhs.toFloat(f16);
- //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
- },
- .f32 => {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
- },
- .f64 => {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
- },
- .f128, .comptime_float, .c_longdouble => {
- const lhs_val = lhs.toFloat(f128);
- const rhs_val = rhs.toFloat(f128);
- return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
- },
- else => unreachable,
- }
-}
-
-pub fn floatMul(
- arena: *Allocator,
- float_type: Type,
- src: LazySrcLoc,
- lhs: Value,
- rhs: Value,
-) !Value {
- _ = src;
- switch (float_type.tag()) {
- .f16 => {
- @panic("TODO add __trunctfhf2 to compiler-rt");
- //const lhs_val = lhs.toFloat(f16);
- //const rhs_val = rhs.toFloat(f16);
- //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
- },
- .f32 => {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
- },
- .f64 => {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
- },
- .f128, .comptime_float, .c_longdouble => {
- const lhs_val = lhs.toFloat(f128);
- const rhs_val = rhs.toFloat(f128);
- return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
- },
- else => unreachable,
- }
-}
-
pub fn simplePtrType(
arena: *Allocator,
elem_ty: Type,
diff --git a/src/Sema.zig b/src/Sema.zig
index 5ad590be6a..6b281f8569 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3479,27 +3479,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = sema.resolveInst(extra.rhs);
- const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
- .ComptimeInt => true,
- .Int => false,
- else => return sema.mod.fail(
- &block.base,
- dest_ty_src,
- "expected integer type, found '{}'",
- .{dest_type},
- ),
- };
-
- const operand_ty = sema.typeOf(operand);
- switch (operand_ty.zigTypeTag()) {
- .ComptimeInt, .Int => {},
- else => return sema.mod.fail(
- &block.base,
- operand_src,
- "expected integer type, found '{}'",
- .{operand_ty},
- ),
- }
+ const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
+ _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
if (try sema.isComptimeKnown(block, operand_src, operand)) {
return sema.coerce(block, dest_type, operand, operand_src);
@@ -4951,30 +4932,30 @@ fn analyzeArithmetic(
const value = switch (zir_tag) {
.add => blk: {
const val = if (is_int)
- try Module.intAdd(sema.arena, lhs_val, rhs_val)
+ try lhs_val.intAdd(rhs_val, sema.arena)
else
- try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
+ try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);
break :blk val;
},
.sub => blk: {
const val = if (is_int)
- try Module.intSub(sema.arena, lhs_val, rhs_val)
+ try lhs_val.intSub(rhs_val, sema.arena)
else
- try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
+ try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);
break :blk val;
},
.div => blk: {
const val = if (is_int)
- try Module.intDiv(sema.arena, lhs_val, rhs_val)
+ try lhs_val.intDiv(rhs_val, sema.arena)
else
- try Module.floatDiv(sema.arena, scalar_type, src, lhs_val, rhs_val);
+ try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);
break :blk val;
},
.mul => blk: {
const val = if (is_int)
- try Module.intMul(sema.arena, lhs_val, rhs_val)
+ try lhs_val.intMul(rhs_val, sema.arena)
else
- try Module.floatMul(sema.arena, scalar_type, src, lhs_val, rhs_val);
+ try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
break :blk val;
},
else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),
@@ -6173,7 +6154,62 @@ fn zirPtrCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});
+ const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
+ const operand = sema.resolveInst(extra.rhs);
+ const operand_ty = sema.typeOf(operand);
+ const mod = sema.mod;
+ const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
+ const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
+
+ if (dest_is_comptime_int) {
+ return sema.coerce(block, dest_ty, operand, operand_src);
+ }
+
+ const target = mod.getTarget();
+ const src_info = operand_ty.intInfo(target);
+ const dest_info = dest_ty.intInfo(target);
+
+ if (src_info.bits == 0 or dest_info.bits == 0) {
+ return sema.addConstant(dest_ty, Value.initTag(.zero));
+ }
+
+ if (!src_is_comptime_int) {
+ if (src_info.signedness != dest_info.signedness) {
+ return mod.fail(&block.base, operand_src, "expected {s} integer type, found '{}'", .{
+ @tagName(dest_info.signedness), operand_ty,
+ });
+ }
+ if (src_info.bits > 0 and src_info.bits < dest_info.bits) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ &block.base,
+ src,
+ "destination type '{}' has more bits than source type '{}'",
+ .{ dest_ty, operand_ty },
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(&block.base, dest_ty_src, msg, "destination type has {d} bits", .{
+ dest_info.bits,
+ });
+ try mod.errNote(&block.base, operand_src, msg, "source type has {d} bits", .{
+ src_info.bits,
+ });
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(dest_ty);
+ return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.bits));
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.trunc, dest_ty, operand);
}
fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6594,6 +6630,14 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
try sema.requireFunctionBlock(block, src);
}
+fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
+ switch (ty.zigTypeTag()) {
+ .ComptimeInt => return true,
+ .Int => return false,
+ else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
+ }
+}
+
fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
if (!ty.isValidVarType(false)) {
return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
diff --git a/src/codegen.zig b/src/codegen.zig
index 8c56ab4431..3864479d2d 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -835,6 +835,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.dbg_stmt => try self.airDbgStmt(inst),
.floatcast => try self.airFloatCast(inst),
.intcast => try self.airIntCast(inst),
+ .trunc => try self.airTrunc(inst),
.bool_to_int => try self.airBoolToInt(inst),
.is_non_null => try self.airIsNonNull(inst),
.is_non_null_ptr => try self.airIsNonNullPtr(inst),
@@ -1109,6 +1110,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
+ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ if (self.liveness.isUnused(inst))
+ return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
+
+ const operand = try self.resolveInst(ty_op.operand);
+ _ = operand;
+ const result: MCValue = switch (arch) {
+ else => return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+ }
+
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 826b73317c..22420aca45 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -900,6 +900,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
.call => try airCall(o, inst),
.dbg_stmt => try airDbgStmt(o, inst),
.intcast => try airIntCast(o, inst),
+ .trunc => try airTrunc(o, inst),
.bool_to_int => try airBoolToInt(o, inst),
.load => try airLoad(o, inst),
.ret => try airRet(o, inst),
@@ -1038,7 +1039,7 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
return CValue.none;
const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const from = try o.resolveInst(ty_op.operand);
+ const operand = try o.resolveInst(ty_op.operand);
const writer = o.writer();
const inst_ty = o.air.typeOfIndex(inst);
@@ -1046,11 +1047,21 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
try writer.writeAll(" = (");
try o.dg.renderType(writer, inst_ty);
try writer.writeAll(")");
- try o.writeCValue(writer, from);
+ try o.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
+fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {
+ if (o.liveness.isUnused(inst))
+ return CValue.none;
+
+ const ty_op = o.air.instructions.items(.data)[inst].ty_op;
+ const operand = try o.resolveInst(ty_op.operand);
+ _ = operand;
+ return o.dg.fail("TODO: C backend: airTrunc", .{});
+}
+
fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
if (o.liveness.isUnused(inst))
return CValue.none;
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 961ed7ee99..8b3edcfe23 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -960,6 +960,7 @@ pub const FuncGen = struct {
.call => try self.airCall(inst),
.cond_br => try self.airCondBr(inst),
.intcast => try self.airIntCast(inst),
+ .trunc => try self.airTrunc(inst),
.floatcast => try self.airFloatCast(inst),
.ptrtoint => try self.airPtrToInt(inst),
.load => try self.airLoad(inst),
@@ -1615,6 +1616,16 @@ pub const FuncGen = struct {
return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");
}
+ fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const operand = try self.resolveInst(ty_op.operand);
+ const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
+ return self.builder.buildTrunc(operand, dest_llvm_ty, "");
+ }
+
fn airFloatCast(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 0977d6128d..4af3cadd84 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -423,6 +423,14 @@ pub const Builder = opaque {
Idx: c_uint,
Name: [*:0]const u8,
) *const Value;
+
+ pub const buildTrunc = LLVMBuildTrunc;
+ extern fn LLVMBuildTrunc(
+ *const Builder,
+ Val: *const Value,
+ DestTy: *const Type,
+ Name: [*:0]const u8,
+ ) *const Value;
};
pub const IntPredicate = enum(c_int) {
diff --git a/src/print_air.zig b/src/print_air.zig
index 5d77c303bb..00317b26e8 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -151,6 +151,7 @@ const Writer = struct {
.load,
.floatcast,
.intcast,
+ .trunc,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
diff --git a/src/value.zig b/src/value.zig
index a0a7c0650c..134b51e494 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -1407,6 +1407,244 @@ pub const Value = extern union {
};
}
+ pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.add(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.sub(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs_q = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
+ );
+ const limbs_r = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len,
+ );
+ const limbs_buffer = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
+ );
+ var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
+ var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
+ result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
+ const result_limbs = result_q.limbs[0..result_q.len];
+
+ if (result_q.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var limbs_buffer = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
+ );
+ defer allocator.free(limbs_buffer);
+ result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn intTrunc(val: Value, arena: *Allocator, bits: u16) !Value {
+ const x = val.toUnsignedInt(); // TODO: implement comptime truncate on big ints
+ if (bits == 64) return val;
+ const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
+ const truncated = x & mask;
+ return Tag.int_u64.create(arena, truncated);
+ }
+
+ pub fn floatAdd(
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+ arena: *Allocator,
+ ) !Value {
+ switch (float_type.tag()) {
+ .f16 => {
+ @panic("TODO add __trunctfhf2 to compiler-rt");
+ //const lhs_val = lhs.toFloat(f16);
+ //const rhs_val = rhs.toFloat(f16);
+ //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
+ },
+ .f32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
+ },
+ .f64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
+ },
+ .f128, .comptime_float, .c_longdouble => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn floatSub(
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+ arena: *Allocator,
+ ) !Value {
+ switch (float_type.tag()) {
+ .f16 => {
+ @panic("TODO add __trunctfhf2 to compiler-rt");
+ //const lhs_val = lhs.toFloat(f16);
+ //const rhs_val = rhs.toFloat(f16);
+ //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
+ },
+ .f32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
+ },
+ .f64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
+ },
+ .f128, .comptime_float, .c_longdouble => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn floatDiv(
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+ arena: *Allocator,
+ ) !Value {
+ switch (float_type.tag()) {
+ .f16 => {
+ @panic("TODO add __trunctfhf2 to compiler-rt");
+ //const lhs_val = lhs.toFloat(f16);
+ //const rhs_val = rhs.toFloat(f16);
+ //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
+ },
+ .f32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
+ },
+ .f64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
+ },
+ .f128, .comptime_float, .c_longdouble => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn floatMul(
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+ arena: *Allocator,
+ ) !Value {
+ switch (float_type.tag()) {
+ .f16 => {
+ @panic("TODO add __trunctfhf2 to compiler-rt");
+ //const lhs_val = lhs.toFloat(f16);
+ //const rhs_val = rhs.toFloat(f16);
+ //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
+ },
+ .f32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
+ },
+ .f64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
+ },
+ .f128, .comptime_float, .c_longdouble => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
+ },
+ else => unreachable,
+ }
+ }
+
/// This type is not copyable since it may contain pointers to its inner data.
pub const Payload = struct {
tag: Tag,
diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig
index 8f0f20cdf5..87de1c9fe5 100644
--- a/test/behavior/basic.zig
+++ b/test/behavior/basic.zig
@@ -1,3 +1,6 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
// normal comment
/// this is a documentation comment
@@ -7,3 +10,11 @@ fn emptyFunctionWithComments() void {}
test "empty function with comments" {
emptyFunctionWithComments();
}
+
+test "truncate" {
+ try expect(testTruncate(0x10fd) == 0xfd);
+ comptime try expect(testTruncate(0x10fd) == 0xfd);
+}
+fn testTruncate(x: u32) u8 {
+ return @truncate(u8, x);
+}
diff --git a/test/behavior/misc.zig b/test/behavior/misc.zig
index ba38d6327c..700e043313 100644
--- a/test/behavior/misc.zig
+++ b/test/behavior/misc.zig
@@ -5,13 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
const mem = std.mem;
const builtin = @import("builtin");
-test "truncate" {
- try expect(testTruncate(0x10fd) == 0xfd);
-}
-fn testTruncate(x: u32) u8 {
- return @truncate(u8, x);
-}
-
fn first4KeysOfHomeRow() []const u8 {
return "aoeu";
}