From 9d79741fd01f7d3c4093ef6d305c84ff5c183a69 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 16 Jul 2020 21:17:43 +0200 Subject: [PATCH 1/6] Stage2: Add support for substraction --- test/stage2/compare_output.zig | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index d013573e30..475dd4594b 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -169,8 +169,38 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + } - // Tests the assert() function. + { + // TODO add a test case for comptime substraction of numbers + var case = ctx.exe("substracting numbers at runtime", linux_x64); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ sub(7, 8); + \\ + \\ exit(); + \\} + \\ + \\fn sub(a: u32, b: u32) void { + \\ if (a - b != 3) unreachable; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("assert function", linux_x64); case.addCompareOutput( \\export fn _start() noreturn { \\ add(3, 4); From 198c09197bf5140e5fb324e1a78a4b5b66d982e5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 16 Jul 2020 21:30:08 +0200 Subject: [PATCH 2/6] Fixed test case --- test/stage2/compare_output.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 475dd4594b..c709712021 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -176,7 +176,7 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.exe("substracting numbers at runtime", linux_x64); case.addCompareOutput( \\export fn _start() noreturn { - \\ sub(7, 8); + \\ sub(7, 4); \\ \\ exit(); \\} From ee7fbb9548fe0e049117f49d10fc0932d175d72d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 17 Jul 2020 23:04:44 +0200 Subject: [PATCH 3/6] Restructured arithmetic operations --- src-self-hosted/Module.zig | 107 +++++++++++++++++++++------------ test/stage2/compare_output.zig | 1 - 2 files changed, 69 insertions(+), 39 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 62fb0df648..fbc180f28d 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3038,7 +3038,6 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn } fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { - return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{}); } fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { @@ -3048,50 +3047,82 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerErro const lhs = try self.resolveInst(scope, inst.positionals.lhs); const rhs = try self.resolveInst(scope, inst.positionals.rhs); - if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and - (rhs.ty.zigTypeTag() == .Int or rhs.ty.zigTypeTag() == .ComptimeInt)) - { - if (!lhs.ty.eql(rhs.ty)) { - return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{}); + const instructions = &[_]*Inst{ lhs, rhs }; + const resolved_type = try self.resolvePeerTypes(scope, instructions); + const resolved_tag = resolved_type.zigTypeTag(); + + const is_int = resolved_tag == .Int or resolved_tag == .ComptimeInt; + + if (!is_int) { + return self.fail(scope, inst.base.src, "TODO analyze arithmetic for types {} and {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() }); + } + + if (lhs.value()) |lhs_val| { + if (rhs.value()) |rhs_val| { + return self.analyzeInstMath(scope, resolved_type, &inst.base, lhs_val, rhs_val); } + } - if (lhs.value()) |lhs_val| { - if (rhs.value()) |rhs_val| { - // 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_val.toBigInt(&lhs_space); - const rhs_bigint = rhs_val.toBigInt(&rhs_space); - const limbs = try scope.arena().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]; + const b = try self.requireRuntimeBlock(scope, inst.base.src); - const val_payload = if (result_bigint.positive) blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - } else blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - }; - - return self.constInst(scope, inst.base.src, .{ - .ty = lhs.ty, - .val = Value.initPayload(val_payload), - }); - } - } + return switch (inst.base.tag) { + .add => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Add, .{ + .lhs = lhs, + .rhs = rhs, + }), + .sub => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Sub, .{ + .lhs = lhs, + .rhs = rhs, + }), + else => self.fail(scope, inst.base.src, "TODO Implement arithmetic for operand {}", .{@tagName(inst.base.tag)}), + }; +} +/// Analyzes operands that are known at comptime +fn analyzeInstMath(self: *Module, scope: *Scope, res_type: Type, base: *zir.Inst, lhs_val: Value, rhs_val: Value) InnerError!*Inst { + // incase rhs is 0, simply return lhs without doing any calculations + // TODO Once division is implemented we should throw an error when dividing by 0. + if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { + return self.constInst(scope, base.src, .{ + .ty = res_type, + .val = lhs_val, + }); const b = try self.requireRuntimeBlock(scope, inst.base.src); return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs); } - return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() }); + + // 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_val.toBigInt(&lhs_space); + const rhs_bigint = rhs_val.toBigInt(&rhs_space); + const limbs = try scope.arena().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 }; + switch (base.tag) { + .add => result_bigint.add(lhs_bigint, rhs_bigint), + .sub => result_bigint.sub(lhs_bigint, rhs_bigint), + else => return error.AnalysisFail, + } + const result_limbs = result_bigint.limbs[0..result_bigint.len]; + + const val_payload = if (result_bigint.positive) blk: { + const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + } else blk: { + const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + }; + + return self.constInst(scope, base.src, .{ + .ty = res_type, + .val = Value.initPayload(val_payload), + }); } fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index c709712021..89e52f4402 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -172,7 +172,6 @@ pub fn addCases(ctx: *TestContext) !void { } { - // TODO add a test case for comptime substraction of numbers var case = ctx.exe("substracting numbers at runtime", linux_x64); case.addCompareOutput( \\export fn _start() noreturn { From 97e92d868e0a4636b95e3a499c7de9f1b84b0ec6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 22 Jul 2020 22:13:11 +0200 Subject: [PATCH 4/6] Rebase and skeleton for float support --- src-self-hosted/Module.zig | 152 +++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 55 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index fbc180f28d..2c8b54d127 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2400,8 +2400,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), - .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), - .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), + .add, .sub => return self.analyzeInstArithmetic(scope, old_inst.cast(zir.Inst.BinOp).?), .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt), .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte), .cmp_eq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_eq).?, .eq), @@ -3037,10 +3036,15 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{}); } -fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn floatOpAllowed(tag: zir.Inst.Tag) bool { + // extend this swich as additional operators are implemented + return switch (tag) { + .add, .sub => true, + else => false, + }; } -fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -3049,80 +3053,118 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerErro const instructions = &[_]*Inst{ lhs, rhs }; const resolved_type = try self.resolvePeerTypes(scope, instructions); - const resolved_tag = resolved_type.zigTypeTag(); - const is_int = resolved_tag == .Int or resolved_tag == .ComptimeInt; + const scalar_type = if (resolved_type.zigTypeTag() == .Vector) + resolved_type.elemType() + else + resolved_type; - if (!is_int) { - return self.fail(scope, inst.base.src, "TODO analyze arithmetic for types {} and {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() }); + const scalar_tag = scalar_type.zigTypeTag(); + + if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { + if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { + return self.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ + lhs.ty.arrayLen(), + rhs.ty.arrayLen(), + }); + } + return self.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); + } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { + return self.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ + lhs.ty, + rhs.ty, + }); + } + + const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; + const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; + + if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { + return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } if (lhs.value()) |lhs_val| { if (rhs.value()) |rhs_val| { - return self.analyzeInstMath(scope, resolved_type, &inst.base, lhs_val, rhs_val); + return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); } } const b = try self.requireRuntimeBlock(scope, inst.base.src); - - return switch (inst.base.tag) { - .add => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Add, .{ - .lhs = lhs, - .rhs = rhs, - }), - .sub => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Sub, .{ - .lhs = lhs, - .rhs = rhs, - }), - else => self.fail(scope, inst.base.src, "TODO Implement arithmetic for operand {}", .{@tagName(inst.base.tag)}), + const ir_tag = switch (inst.base.tag) { + .add => Inst.Tag.add, + .sub => Inst.Tag.sub, + else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), }; + + if (is_float) { + // Implicit cast the smaller one to the larger one. + const dest_type = x: { + if (lhs.ty.zigTypeTag() == .ComptimeFloat) { + break :x rhs.ty; + } else if (rhs.ty.zigTypeTag() == .ComptimeFloat) { + break :x lhs.ty; + } + if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { + break :x lhs.ty; + } else { + break :x rhs.ty; + } + }; + const casted_lhs = try self.coerce(scope, dest_type, lhs); + const casted_rhs = try self.coerce(scope, dest_type, rhs); + return self.addBinOp(b, inst.base.src, dest_type, ir_tag, casted_lhs, casted_rhs); + } + + return self.addBinOp(b, inst.base.src, resolved_type, ir_tag, lhs, rhs); } /// Analyzes operands that are known at comptime -fn analyzeInstMath(self: *Module, scope: *Scope, res_type: Type, base: *zir.Inst, lhs_val: Value, rhs_val: Value) InnerError!*Inst { +fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { // incase rhs is 0, simply return lhs without doing any calculations // TODO Once division is implemented we should throw an error when dividing by 0. if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { - return self.constInst(scope, base.src, .{ + return self.constInst(scope, inst.base.src, .{ .ty = res_type, .val = lhs_val, }); - const b = try self.requireRuntimeBlock(scope, inst.base.src); - return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs); } - // 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_val.toBigInt(&lhs_space); - const rhs_bigint = rhs_val.toBigInt(&rhs_space); - const limbs = try scope.arena().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 }; - switch (base.tag) { - .add => result_bigint.add(lhs_bigint, rhs_bigint), - .sub => result_bigint.sub(lhs_bigint, rhs_bigint), - else => return error.AnalysisFail, + if (lhs_val.isFloat() or res_type.tag() == .comptime_float) { + return self.fail(scope, inst.base.src, "TODO implement arithmetic for floats", .{}); + } else { + // 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_val.toBigInt(&lhs_space); + const rhs_bigint = rhs_val.toBigInt(&rhs_space); + const limbs = try scope.arena().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 }; + switch (inst.base.tag) { + .add => result_bigint.add(lhs_bigint, rhs_bigint), + .sub => result_bigint.sub(lhs_bigint, rhs_bigint), + else => return error.AnalysisFail, + } + const result_limbs = result_bigint.limbs[0..result_bigint.len]; + + const val_payload = if (result_bigint.positive) blk: { + const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + } else blk: { + const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + }; + + return self.constInst(scope, inst.base.src, .{ + .ty = res_type, + .val = Value.initPayload(val_payload), + }); } - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - const val_payload = if (result_bigint.positive) blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - } else blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - }; - - return self.constInst(scope, base.src, .{ - .ty = res_type, - .val = Value.initPayload(val_payload), - }); } fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { From 470264a4f57bf667553678cd51d8c70945b96fe4 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 23 Jul 2020 19:25:22 +0200 Subject: [PATCH 5/6] Restructuring and f32/f64 support --- src-self-hosted/Module.zig | 218 +++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 57 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 2c8b54d127..eb17c64fa8 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3053,6 +3053,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In const instructions = &[_]*Inst{ lhs, rhs }; const resolved_type = try self.resolvePeerTypes(scope, instructions); + const casted_lhs = try self.coerce(scope, resolved_type, lhs); + const casted_rhs = try self.coerce(scope, resolved_type, rhs); const scalar_type = if (resolved_type.zigTypeTag() == .Vector) resolved_type.elemType() @@ -3083,8 +3085,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } - if (lhs.value()) |lhs_val| { - if (rhs.value()) |rhs_val| { + if (casted_lhs.value()) |lhs_val| { + if (casted_rhs.value()) |rhs_val| { return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); } } @@ -3096,26 +3098,7 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), }; - if (is_float) { - // Implicit cast the smaller one to the larger one. - const dest_type = x: { - if (lhs.ty.zigTypeTag() == .ComptimeFloat) { - break :x rhs.ty; - } else if (rhs.ty.zigTypeTag() == .ComptimeFloat) { - break :x lhs.ty; - } - if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { - break :x lhs.ty; - } else { - break :x rhs.ty; - } - }; - const casted_lhs = try self.coerce(scope, dest_type, lhs); - const casted_rhs = try self.coerce(scope, dest_type, rhs); - return self.addBinOp(b, inst.base.src, dest_type, ir_tag, casted_lhs, casted_rhs); - } - - return self.addBinOp(b, inst.base.src, resolved_type, ir_tag, lhs, rhs); + return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); } /// Analyzes operands that are known at comptime @@ -3128,43 +3111,30 @@ fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.In .val = lhs_val, }); } + const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; - if (lhs_val.isFloat() or res_type.tag() == .comptime_float) { - return self.fail(scope, inst.base.src, "TODO implement arithmetic for floats", .{}); - } else { - // 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_val.toBigInt(&lhs_space); - const rhs_bigint = rhs_val.toBigInt(&rhs_space); - const limbs = try scope.arena().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 }; - switch (inst.base.tag) { - .add => result_bigint.add(lhs_bigint, rhs_bigint), - .sub => result_bigint.sub(lhs_bigint, rhs_bigint), - else => return error.AnalysisFail, - } - const result_limbs = result_bigint.limbs[0..result_bigint.len]; + const value = try switch (inst.base.tag) { + .add => blk: { + const val = if (is_int) + bigIntAdd(scope.arena(), lhs_val, rhs_val) + else + floatAdd(self.target(), scope.arena(), res_type, lhs_val, rhs_val); + break :blk val; + }, + .sub => blk: { + const val = if (is_int) + bigIntSub(scope.arena(), lhs_val, rhs_val) + else + floatSub(self.target(), scope.arena(), res_type, lhs_val, rhs_val); + break :blk val; + }, + else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), + }; - const val_payload = if (result_bigint.positive) blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - } else blk: { - const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); - val_payload.* = .{ .limbs = result_limbs }; - break :blk &val_payload.base; - }; - - return self.constInst(scope, inst.base.src, .{ - .ty = res_type, - .val = Value.initPayload(val_payload), - }); - } + return self.constInst(scope, inst.base.src, .{ + .ty = res_type, + .val = value, + }); } fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { @@ -3586,6 +3556,18 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { prev_inst = next_inst; continue; } + if (prev_inst.ty.isInt() and next_inst.ty.isInt()) { + if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) { + prev_inst = next_inst; + } + continue; + } + if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { + if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) { + prev_inst = next_inst; + } + continue; + } // TODO error notes pointing out each type return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); @@ -3836,3 +3818,125 @@ pub const ErrorMsg = struct { fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { return @bitCast(u128, a) == @bitCast(u128, b); } + +fn bigIntAdd(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]; + + const val_payload = if (result_bigint.positive) blk: { + const val_payload = try allocator.create(Value.Payload.IntBigPositive); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + } else blk: { + const val_payload = try allocator.create(Value.Payload.IntBigNegative); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + }; + + return Value.initPayload(val_payload); +} + +fn bigIntSub(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]; + + const val_payload = if (result_bigint.positive) blk: { + const val_payload = try allocator.create(Value.Payload.IntBigPositive); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + } else blk: { + const val_payload = try allocator.create(Value.Payload.IntBigNegative); + val_payload.* = .{ .limbs = result_limbs }; + break :blk &val_payload.base; + }; + + return Value.initPayload(val_payload); +} + +fn floatAdd(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { + var bit_count = switch (float_type.tag()) { + .comptime_float => 128, + else => float_type.floatBits(cur_target), + }; + + const val_payload = switch (bit_count) { + 16 => { + @panic("TODO soft float"); + }, + 32 => blk: { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + const val_payload = try allocator.create(Value.Payload.Float_32); + val_payload.* = .{ .val = lhs_val + rhs_val }; + break :blk &val_payload.base; + }, + 64 => blk: { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + const val_payload = try allocator.create(Value.Payload.Float_64); + val_payload.* = .{ .val = lhs_val + rhs_val }; + break :blk &val_payload.base; + }, + 128 => blk: { + @panic("TODO Big float"); + }, + else => unreachable, + }; + + return Value.initPayload(val_payload); +} + +fn floatSub(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { + var bit_count = switch (float_type.tag()) { + .comptime_float => 128, + else => float_type.floatBits(cur_target), + }; + + const val_payload = switch (bit_count) { + 16 => { + @panic("TODO soft float"); + }, + 32 => blk: { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + const val_payload = try allocator.create(Value.Payload.Float_32); + val_payload.* = .{ .val = lhs_val - rhs_val }; + break :blk &val_payload.base; + }, + 64 => blk: { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + const val_payload = try allocator.create(Value.Payload.Float_64); + val_payload.* = .{ .val = lhs_val - rhs_val }; + break :blk &val_payload.base; + }, + 128 => blk: { + @panic("TODO Big float"); + }, + else => unreachable, + }; + + return Value.initPayload(val_payload); +} From 3019ab93917098c94fb7507c1aadeee330e5982a Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 24 Jul 2020 17:51:24 +0200 Subject: [PATCH 6/6] Fix resolvepeertype() int signess and feedback improvements --- src-self-hosted/Module.zig | 39 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index eb17c64fa8..2854505f70 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3087,7 +3087,7 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In if (casted_lhs.value()) |lhs_val| { if (casted_rhs.value()) |rhs_val| { - return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); + return self.analyzeInstComptimeOp(scope, scalar_type, inst, lhs_val, rhs_val); } } @@ -3102,7 +3102,7 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In } /// Analyzes operands that are known at comptime -fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { +fn analyzeInstComptimeOp(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { // incase rhs is 0, simply return lhs without doing any calculations // TODO Once division is implemented we should throw an error when dividing by 0. if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { @@ -3116,16 +3116,16 @@ fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.In const value = try switch (inst.base.tag) { .add => blk: { const val = if (is_int) - bigIntAdd(scope.arena(), lhs_val, rhs_val) + intAdd(scope.arena(), lhs_val, rhs_val) else - floatAdd(self.target(), scope.arena(), res_type, lhs_val, rhs_val); + self.floatAdd(scope, res_type, inst, lhs_val, rhs_val); break :blk val; }, .sub => blk: { const val = if (is_int) - bigIntSub(scope.arena(), lhs_val, rhs_val) + intSub(scope.arena(), lhs_val, rhs_val) else - floatSub(self.target(), scope.arena(), res_type, lhs_val, rhs_val); + self.floatSub(scope, res_type, inst, lhs_val, rhs_val); break :blk val; }, else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), @@ -3556,7 +3556,10 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { prev_inst = next_inst; continue; } - if (prev_inst.ty.isInt() and next_inst.ty.isInt()) { + if (prev_inst.ty.isInt() and + next_inst.ty.isInt() and + prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) + { if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) { prev_inst = next_inst; } @@ -3819,7 +3822,7 @@ fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { return @bitCast(u128, a) == @bitCast(u128, b); } -fn bigIntAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { +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; @@ -3847,7 +3850,7 @@ fn bigIntAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { return Value.initPayload(val_payload); } -fn bigIntSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { +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; @@ -3875,15 +3878,16 @@ fn bigIntSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { return Value.initPayload(val_payload); } -fn floatAdd(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { +fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value { var bit_count = switch (float_type.tag()) { .comptime_float => 128, - else => float_type.floatBits(cur_target), + else => float_type.floatBits(self.target()), }; + const allocator = scope.arena(); const val_payload = switch (bit_count) { 16 => { - @panic("TODO soft float"); + return self.fail(scope, inst.base.src, "TODO Implement addition for soft floats", .{}); }, 32 => blk: { const lhs_val = lhs.toFloat(f32); @@ -3900,7 +3904,7 @@ fn floatAdd(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Va break :blk &val_payload.base; }, 128 => blk: { - @panic("TODO Big float"); + return self.fail(scope, inst.base.src, "TODO Implement addition for big floats", .{}); }, else => unreachable, }; @@ -3908,15 +3912,16 @@ fn floatAdd(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Va return Value.initPayload(val_payload); } -fn floatSub(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { +fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value { var bit_count = switch (float_type.tag()) { .comptime_float => 128, - else => float_type.floatBits(cur_target), + else => float_type.floatBits(self.target()), }; + const allocator = scope.arena(); const val_payload = switch (bit_count) { 16 => { - @panic("TODO soft float"); + return self.fail(scope, inst.base.src, "TODO Implement substraction for soft floats", .{}); }, 32 => blk: { const lhs_val = lhs.toFloat(f32); @@ -3933,7 +3938,7 @@ fn floatSub(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Va break :blk &val_payload.base; }, 128 => blk: { - @panic("TODO Big float"); + return self.fail(scope, inst.base.src, "TODO Implement substraction for big floats", .{}); }, else => unreachable, };