authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-24 18:29:52+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-24 18:29:52+00:00
logdf1a2ecd3bfea5506151f62780f442dbe487db96
tree1dc72ad2bd33a893e98a18c4da70b886e7ce8913
parenta1d72fad81c17fcaadd9da782b63f0ce2638f1bb
parent3019ab93917098c94fb7507c1aadeee330e5982a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5891 from Luukdegram/stage2-substraction

Stage2: Substraction support

2 files changed, 255 insertions(+), 44 deletions(-)

src-self-hosted/Module.zig+225-43
...@@ -2400,8 +2400,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -2400,8 +2400,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
2400 .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?),2400 .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?),
2401 .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?),2401 .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?),
2402 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?),2402 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?),
2403 .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?),2403 .add, .sub => return self.analyzeInstArithmetic(scope, old_inst.cast(zir.Inst.BinOp).?),
2404 .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?),
2405 .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt),2404 .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt),
2406 .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte),2405 .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte),
2407 .cmp_eq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_eq).?, .eq),2406 .cmp_eq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_eq).?, .eq),
...@@ -3037,61 +3036,105 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn...@@ -3037,61 +3036,105 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn
3037 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});3036 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
3038}3037}
30393038
3040fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {3039fn floatOpAllowed(tag: zir.Inst.Tag) bool {
3041 return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{});3040 // extend this swich as additional operators are implemented
3041 return switch (tag) {
3042 .add, .sub => true,
3043 else => false,
3044 };
3042}3045}
30433046
3044fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {3047fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3045 const tracy = trace(@src());3048 const tracy = trace(@src());
3046 defer tracy.end();3049 defer tracy.end();
30473050
3048 const lhs = try self.resolveInst(scope, inst.positionals.lhs);3051 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
3049 const rhs = try self.resolveInst(scope, inst.positionals.rhs);3052 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
30503053
3051 if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and3054 const instructions = &[_]*Inst{ lhs, rhs };
3052 (rhs.ty.zigTypeTag() == .Int or rhs.ty.zigTypeTag() == .ComptimeInt))3055 const resolved_type = try self.resolvePeerTypes(scope, instructions);
3053 {3056 const casted_lhs = try self.coerce(scope, resolved_type, lhs);
3054 if (!lhs.ty.eql(rhs.ty)) {3057 const casted_rhs = try self.coerce(scope, resolved_type, rhs);
3055 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});3058
3059 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3060 resolved_type.elemType()
3061 else
3062 resolved_type;
3063
3064 const scalar_tag = scalar_type.zigTypeTag();
3065
3066 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3067 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3068 return self.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
3069 lhs.ty.arrayLen(),
3070 rhs.ty.arrayLen(),
3071 });
3056 }3072 }
3073 return self.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
3074 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3075 return self.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
3076 lhs.ty,
3077 rhs.ty,
3078 });
3079 }
30573080
3058 if (lhs.value()) |lhs_val| {3081 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3059 if (rhs.value()) |rhs_val| {3082 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
3060 // TODO is this a performance issue? maybe we should try the operation without
3061 // resorting to BigInt first.
3062 var lhs_space: Value.BigIntSpace = undefined;
3063 var rhs_space: Value.BigIntSpace = undefined;
3064 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
3065 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
3066 const limbs = try scope.arena().alloc(
3067 std.math.big.Limb,
3068 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
3069 );
3070 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
3071 result_bigint.add(lhs_bigint, rhs_bigint);
3072 const result_limbs = result_bigint.limbs[0..result_bigint.len];
3073
3074 const val_payload = if (result_bigint.positive) blk: {
3075 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
3076 val_payload.* = .{ .limbs = result_limbs };
3077 break :blk &val_payload.base;
3078 } else blk: {
3079 const val_payload = try scope.arena().create(Value.Payload.IntBigNegative);
3080 val_payload.* = .{ .limbs = result_limbs };
3081 break :blk &val_payload.base;
3082 };
30833083
3084 return self.constInst(scope, inst.base.src, .{3084 if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
3085 .ty = lhs.ty,3085 return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3086 .val = Value.initPayload(val_payload),3086 }
3087 });3087
3088 }3088 if (casted_lhs.value()) |lhs_val| {
3089 if (casted_rhs.value()) |rhs_val| {
3090 return self.analyzeInstComptimeOp(scope, scalar_type, inst, lhs_val, rhs_val);
3089 }3091 }
3092 }
30903093
3091 const b = try self.requireRuntimeBlock(scope, inst.base.src);3094 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3092 return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs);3095 const ir_tag = switch (inst.base.tag) {
3096 .add => Inst.Tag.add,
3097 .sub => Inst.Tag.sub,
3098 else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}),
3099 };
3100
3101 return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3102}
3103
3104/// Analyzes operands that are known at comptime
3105fn analyzeInstComptimeOp(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
3106 // incase rhs is 0, simply return lhs without doing any calculations
3107 // TODO Once division is implemented we should throw an error when dividing by 0.
3108 if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) {
3109 return self.constInst(scope, inst.base.src, .{
3110 .ty = res_type,
3111 .val = lhs_val,
3112 });
3093 }3113 }
3094 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });3114 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
3115
3116 const value = try switch (inst.base.tag) {
3117 .add => blk: {
3118 const val = if (is_int)
3119 intAdd(scope.arena(), lhs_val, rhs_val)
3120 else
3121 self.floatAdd(scope, res_type, inst, lhs_val, rhs_val);
3122 break :blk val;
3123 },
3124 .sub => blk: {
3125 const val = if (is_int)
3126 intSub(scope.arena(), lhs_val, rhs_val)
3127 else
3128 self.floatSub(scope, res_type, inst, lhs_val, rhs_val);
3129 break :blk val;
3130 },
3131 else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}),
3132 };
3133
3134 return self.constInst(scope, inst.base.src, .{
3135 .ty = res_type,
3136 .val = value,
3137 });
3095}3138}
30963139
3097fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {3140fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
...@@ -3513,6 +3556,21 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {...@@ -3513,6 +3556,21 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3513 prev_inst = next_inst;3556 prev_inst = next_inst;
3514 continue;3557 continue;
3515 }3558 }
3559 if (prev_inst.ty.isInt() and
3560 next_inst.ty.isInt() and
3561 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
3562 {
3563 if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) {
3564 prev_inst = next_inst;
3565 }
3566 continue;
3567 }
3568 if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
3569 if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) {
3570 prev_inst = next_inst;
3571 }
3572 continue;
3573 }
35163574
3517 // TODO error notes pointing out each type3575 // TODO error notes pointing out each type
3518 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });3576 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
...@@ -3763,3 +3821,127 @@ pub const ErrorMsg = struct {...@@ -3763,3 +3821,127 @@ pub const ErrorMsg = struct {
3763fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {3821fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
3764 return @bitCast(u128, a) == @bitCast(u128, b);3822 return @bitCast(u128, a) == @bitCast(u128, b);
3765}3823}
3824
3825fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3826 // TODO is this a performance issue? maybe we should try the operation without
3827 // resorting to BigInt first.
3828 var lhs_space: Value.BigIntSpace = undefined;
3829 var rhs_space: Value.BigIntSpace = undefined;
3830 const lhs_bigint = lhs.toBigInt(&lhs_space);
3831 const rhs_bigint = rhs.toBigInt(&rhs_space);
3832 const limbs = try allocator.alloc(
3833 std.math.big.Limb,
3834 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
3835 );
3836 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
3837 result_bigint.add(lhs_bigint, rhs_bigint);
3838 const result_limbs = result_bigint.limbs[0..result_bigint.len];
3839
3840 const val_payload = if (result_bigint.positive) blk: {
3841 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
3842 val_payload.* = .{ .limbs = result_limbs };
3843 break :blk &val_payload.base;
3844 } else blk: {
3845 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
3846 val_payload.* = .{ .limbs = result_limbs };
3847 break :blk &val_payload.base;
3848 };
3849
3850 return Value.initPayload(val_payload);
3851}
3852
3853fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3854 // TODO is this a performance issue? maybe we should try the operation without
3855 // resorting to BigInt first.
3856 var lhs_space: Value.BigIntSpace = undefined;
3857 var rhs_space: Value.BigIntSpace = undefined;
3858 const lhs_bigint = lhs.toBigInt(&lhs_space);
3859 const rhs_bigint = rhs.toBigInt(&rhs_space);
3860 const limbs = try allocator.alloc(
3861 std.math.big.Limb,
3862 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
3863 );
3864 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
3865 result_bigint.sub(lhs_bigint, rhs_bigint);
3866 const result_limbs = result_bigint.limbs[0..result_bigint.len];
3867
3868 const val_payload = if (result_bigint.positive) blk: {
3869 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
3870 val_payload.* = .{ .limbs = result_limbs };
3871 break :blk &val_payload.base;
3872 } else blk: {
3873 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
3874 val_payload.* = .{ .limbs = result_limbs };
3875 break :blk &val_payload.base;
3876 };
3877
3878 return Value.initPayload(val_payload);
3879}
3880
3881fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {
3882 var bit_count = switch (float_type.tag()) {
3883 .comptime_float => 128,
3884 else => float_type.floatBits(self.target()),
3885 };
3886
3887 const allocator = scope.arena();
3888 const val_payload = switch (bit_count) {
3889 16 => {
3890 return self.fail(scope, inst.base.src, "TODO Implement addition for soft floats", .{});
3891 },
3892 32 => blk: {
3893 const lhs_val = lhs.toFloat(f32);
3894 const rhs_val = rhs.toFloat(f32);
3895 const val_payload = try allocator.create(Value.Payload.Float_32);
3896 val_payload.* = .{ .val = lhs_val + rhs_val };
3897 break :blk &val_payload.base;
3898 },
3899 64 => blk: {
3900 const lhs_val = lhs.toFloat(f64);
3901 const rhs_val = rhs.toFloat(f64);
3902 const val_payload = try allocator.create(Value.Payload.Float_64);
3903 val_payload.* = .{ .val = lhs_val + rhs_val };
3904 break :blk &val_payload.base;
3905 },
3906 128 => blk: {
3907 return self.fail(scope, inst.base.src, "TODO Implement addition for big floats", .{});
3908 },
3909 else => unreachable,
3910 };
3911
3912 return Value.initPayload(val_payload);
3913}
3914
3915fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {
3916 var bit_count = switch (float_type.tag()) {
3917 .comptime_float => 128,
3918 else => float_type.floatBits(self.target()),
3919 };
3920
3921 const allocator = scope.arena();
3922 const val_payload = switch (bit_count) {
3923 16 => {
3924 return self.fail(scope, inst.base.src, "TODO Implement substraction for soft floats", .{});
3925 },
3926 32 => blk: {
3927 const lhs_val = lhs.toFloat(f32);
3928 const rhs_val = rhs.toFloat(f32);
3929 const val_payload = try allocator.create(Value.Payload.Float_32);
3930 val_payload.* = .{ .val = lhs_val - rhs_val };
3931 break :blk &val_payload.base;
3932 },
3933 64 => blk: {
3934 const lhs_val = lhs.toFloat(f64);
3935 const rhs_val = rhs.toFloat(f64);
3936 const val_payload = try allocator.create(Value.Payload.Float_64);
3937 val_payload.* = .{ .val = lhs_val - rhs_val };
3938 break :blk &val_payload.base;
3939 },
3940 128 => blk: {
3941 return self.fail(scope, inst.base.src, "TODO Implement substraction for big floats", .{});
3942 },
3943 else => unreachable,
3944 };
3945
3946 return Value.initPayload(val_payload);
3947}
test/stage2/compare_output.zig+30-1
...@@ -169,8 +169,37 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -169,8 +169,37 @@ pub fn addCases(ctx: *TestContext) !void {
169 ,169 ,
170 "",170 "",
171 );171 );
172 }
172173
173 // Tests the assert() function.174 {
175 var case = ctx.exe("substracting numbers at runtime", linux_x64);
176 case.addCompareOutput(
177 \\export fn _start() noreturn {
178 \\ sub(7, 4);
179 \\
180 \\ exit();
181 \\}
182 \\
183 \\fn sub(a: u32, b: u32) void {
184 \\ if (a - b != 3) unreachable;
185 \\}
186 \\
187 \\fn exit() noreturn {
188 \\ asm volatile ("syscall"
189 \\ :
190 \\ : [number] "{rax}" (231),
191 \\ [arg1] "{rdi}" (0)
192 \\ : "rcx", "r11", "memory"
193 \\ );
194 \\ unreachable;
195 \\}
196 ,
197 "",
198 );
199 }
200
201 {
202 var case = ctx.exe("assert function", linux_x64);
174 case.addCompareOutput(203 case.addCompareOutput(
175 \\export fn _start() noreturn {204 \\export fn _start() noreturn {
176 \\ add(3, 4);205 \\ add(3, 4);