diff options
| -rw-r--r-- | src/Air.zig | 11 | ||||
| -rw-r--r-- | src/Liveness.zig | 1 | ||||
| -rw-r--r-- | src/Module.zig | 238 | ||||
| -rw-r--r-- | src/Sema.zig | 104 | ||||
| -rw-r--r-- | src/codegen.zig | 14 | ||||
| -rw-r--r-- | src/codegen/c.zig | 15 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 11 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 8 | ||||
| -rw-r--r-- | src/print_air.zig | 1 | ||||
| -rw-r--r-- | src/value.zig | 238 | ||||
| -rw-r--r-- | test/behavior/basic.zig | 11 | ||||
| -rw-r--r-- | test/behavior/misc.zig | 7 |
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"; } |
