| ... | @@ -3053,6 +3053,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In | ... | @@ -3053,6 +3053,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In |
| 3053 | | 3053 | |
| 3054 | const instructions = &[_]*Inst{ lhs, rhs }; | 3054 | const instructions = &[_]*Inst{ lhs, rhs }; |
| 3055 | const resolved_type = try self.resolvePeerTypes(scope, instructions); | 3055 | const resolved_type = try self.resolvePeerTypes(scope, instructions); |
| | 3056 | const casted_lhs = try self.coerce(scope, resolved_type, lhs); |
| | 3057 | const casted_rhs = try self.coerce(scope, resolved_type, rhs); |
| 3056 | | 3058 | |
| 3057 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | 3059 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) |
| 3058 | resolved_type.elemType() | 3060 | resolved_type.elemType() |
| ... | @@ -3083,8 +3085,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In | ... | @@ -3083,8 +3085,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In |
| 3083 | return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | 3085 | return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); |
| 3084 | } | 3086 | } |
| 3085 | | 3087 | |
| 3086 | if (lhs.value()) |lhs_val| { | 3088 | if (casted_lhs.value()) |lhs_val| { |
| 3087 | if (rhs.value()) |rhs_val| { | 3089 | if (casted_rhs.value()) |rhs_val| { |
| 3088 | return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); | 3090 | return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); |
| 3089 | } | 3091 | } |
| 3090 | } | 3092 | } |
| ... | @@ -3096,26 +3098,7 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In | ... | @@ -3096,26 +3098,7 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In |
| 3096 | else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), | 3098 | else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), |
| 3097 | }; | 3099 | }; |
| 3098 | | 3100 | |
| 3099 | if (is_float) { | 3101 | return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); |
| 3100 | // Implicit cast the smaller one to the larger one. | | |
| 3101 | const dest_type = x: { | | |
| 3102 | if (lhs.ty.zigTypeTag() == .ComptimeFloat) { | | |
| 3103 | break :x rhs.ty; | | |
| 3104 | } else if (rhs.ty.zigTypeTag() == .ComptimeFloat) { | | |
| 3105 | break :x lhs.ty; | | |
| 3106 | } | | |
| 3107 | if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { | | |
| 3108 | break :x lhs.ty; | | |
| 3109 | } else { | | |
| 3110 | break :x rhs.ty; | | |
| 3111 | } | | |
| 3112 | }; | | |
| 3113 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | | |
| 3114 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | | |
| 3115 | return self.addBinOp(b, inst.base.src, dest_type, ir_tag, casted_lhs, casted_rhs); | | |
| 3116 | } | | |
| 3117 | | | |
| 3118 | return self.addBinOp(b, inst.base.src, resolved_type, ir_tag, lhs, rhs); | | |
| 3119 | } | 3102 | } |
| 3120 | | 3103 | |
| 3121 | /// Analyzes operands that are known at comptime | 3104 | /// Analyzes operands that are known at comptime |
| ... | @@ -3128,43 +3111,30 @@ fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.In | ... | @@ -3128,43 +3111,30 @@ fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.In |
| 3128 | .val = lhs_val, | 3111 | .val = lhs_val, |
| 3129 | }); | 3112 | }); |
| 3130 | } | 3113 | } |
| | 3114 | const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; |
| 3131 | | 3115 | |
| 3132 | if (lhs_val.isFloat() or res_type.tag() == .comptime_float) { | 3116 | const value = try switch (inst.base.tag) { |
| 3133 | return self.fail(scope, inst.base.src, "TODO implement arithmetic for floats", .{}); | 3117 | .add => blk: { |
| 3134 | } else { | 3118 | const val = if (is_int) |
| 3135 | // TODO is this a performance issue? maybe we should try the operation without | 3119 | bigIntAdd(scope.arena(), lhs_val, rhs_val) |
| 3136 | // resorting to BigInt first. | 3120 | else |
| 3137 | var lhs_space: Value.BigIntSpace = undefined; | 3121 | floatAdd(self.target(), scope.arena(), res_type, lhs_val, rhs_val); |
| 3138 | var rhs_space: Value.BigIntSpace = undefined; | 3122 | break :blk val; |
| 3139 | const lhs_bigint = lhs_val.toBigInt(&lhs_space); | 3123 | }, |
| 3140 | const rhs_bigint = rhs_val.toBigInt(&rhs_space); | 3124 | .sub => blk: { |
| 3141 | const limbs = try scope.arena().alloc( | 3125 | const val = if (is_int) |
| 3142 | std.math.big.Limb, | 3126 | bigIntSub(scope.arena(), lhs_val, rhs_val) |
| 3143 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | 3127 | else |
| 3144 | ); | 3128 | floatSub(self.target(), scope.arena(), res_type, lhs_val, rhs_val); |
| 3145 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | 3129 | break :blk val; |
| 3146 | switch (inst.base.tag) { | 3130 | }, |
| 3147 | .add => result_bigint.add(lhs_bigint, rhs_bigint), | 3131 | else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), |
| 3148 | .sub => result_bigint.sub(lhs_bigint, rhs_bigint), | 3132 | }; |
| 3149 | else => return error.AnalysisFail, | | |
| 3150 | } | | |
| 3151 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | | |
| 3152 | | | |
| 3153 | const val_payload = if (result_bigint.positive) blk: { | | |
| 3154 | const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); | | |
| 3155 | val_payload.* = .{ .limbs = result_limbs }; | | |
| 3156 | break :blk &val_payload.base; | | |
| 3157 | } else blk: { | | |
| 3158 | const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); | | |
| 3159 | val_payload.* = .{ .limbs = result_limbs }; | | |
| 3160 | break :blk &val_payload.base; | | |
| 3161 | }; | | |
| 3162 | | 3133 | |
| 3163 | return self.constInst(scope, inst.base.src, .{ | 3134 | return self.constInst(scope, inst.base.src, .{ |
| 3164 | .ty = res_type, | 3135 | .ty = res_type, |
| 3165 | .val = Value.initPayload(val_payload), | 3136 | .val = value, |
| 3166 | }); | 3137 | }); |
| 3167 | } | | |
| 3168 | } | 3138 | } |
| 3169 | | 3139 | |
| 3170 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { | 3140 | 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 { | ... | @@ -3586,6 +3556,18 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { |
| 3586 | prev_inst = next_inst; | 3556 | prev_inst = next_inst; |
| 3587 | continue; | 3557 | continue; |
| 3588 | } | 3558 | } |
| | 3559 | if (prev_inst.ty.isInt() and next_inst.ty.isInt()) { |
| | 3560 | if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) { |
| | 3561 | prev_inst = next_inst; |
| | 3562 | } |
| | 3563 | continue; |
| | 3564 | } |
| | 3565 | if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { |
| | 3566 | if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) { |
| | 3567 | prev_inst = next_inst; |
| | 3568 | } |
| | 3569 | continue; |
| | 3570 | } |
| 3589 | | 3571 | |
| 3590 | // TODO error notes pointing out each type | 3572 | // TODO error notes pointing out each type |
| 3591 | return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); | 3573 | return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); |
| ... | @@ -3836,3 +3818,125 @@ pub const ErrorMsg = struct { | ... | @@ -3836,3 +3818,125 @@ pub const ErrorMsg = struct { |
| 3836 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | 3818 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3837 | return @bitCast(u128, a) == @bitCast(u128, b); | 3819 | return @bitCast(u128, a) == @bitCast(u128, b); |
| 3838 | } | 3820 | } |
| | 3821 | |
| | 3822 | fn bigIntAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| | 3823 | // TODO is this a performance issue? maybe we should try the operation without |
| | 3824 | // resorting to BigInt first. |
| | 3825 | var lhs_space: Value.BigIntSpace = undefined; |
| | 3826 | var rhs_space: Value.BigIntSpace = undefined; |
| | 3827 | const lhs_bigint = lhs.toBigInt(&lhs_space); |
| | 3828 | const rhs_bigint = rhs.toBigInt(&rhs_space); |
| | 3829 | const limbs = try allocator.alloc( |
| | 3830 | std.math.big.Limb, |
| | 3831 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, |
| | 3832 | ); |
| | 3833 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| | 3834 | result_bigint.add(lhs_bigint, rhs_bigint); |
| | 3835 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; |
| | 3836 | |
| | 3837 | const val_payload = if (result_bigint.positive) blk: { |
| | 3838 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); |
| | 3839 | val_payload.* = .{ .limbs = result_limbs }; |
| | 3840 | break :blk &val_payload.base; |
| | 3841 | } else blk: { |
| | 3842 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); |
| | 3843 | val_payload.* = .{ .limbs = result_limbs }; |
| | 3844 | break :blk &val_payload.base; |
| | 3845 | }; |
| | 3846 | |
| | 3847 | return Value.initPayload(val_payload); |
| | 3848 | } |
| | 3849 | |
| | 3850 | fn bigIntSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| | 3851 | // TODO is this a performance issue? maybe we should try the operation without |
| | 3852 | // resorting to BigInt first. |
| | 3853 | var lhs_space: Value.BigIntSpace = undefined; |
| | 3854 | var rhs_space: Value.BigIntSpace = undefined; |
| | 3855 | const lhs_bigint = lhs.toBigInt(&lhs_space); |
| | 3856 | const rhs_bigint = rhs.toBigInt(&rhs_space); |
| | 3857 | const limbs = try allocator.alloc( |
| | 3858 | std.math.big.Limb, |
| | 3859 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, |
| | 3860 | ); |
| | 3861 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| | 3862 | result_bigint.sub(lhs_bigint, rhs_bigint); |
| | 3863 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; |
| | 3864 | |
| | 3865 | const val_payload = if (result_bigint.positive) blk: { |
| | 3866 | const val_payload = try allocator.create(Value.Payload.IntBigPositive); |
| | 3867 | val_payload.* = .{ .limbs = result_limbs }; |
| | 3868 | break :blk &val_payload.base; |
| | 3869 | } else blk: { |
| | 3870 | const val_payload = try allocator.create(Value.Payload.IntBigNegative); |
| | 3871 | val_payload.* = .{ .limbs = result_limbs }; |
| | 3872 | break :blk &val_payload.base; |
| | 3873 | }; |
| | 3874 | |
| | 3875 | return Value.initPayload(val_payload); |
| | 3876 | } |
| | 3877 | |
| | 3878 | fn floatAdd(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { |
| | 3879 | var bit_count = switch (float_type.tag()) { |
| | 3880 | .comptime_float => 128, |
| | 3881 | else => float_type.floatBits(cur_target), |
| | 3882 | }; |
| | 3883 | |
| | 3884 | const val_payload = switch (bit_count) { |
| | 3885 | 16 => { |
| | 3886 | @panic("TODO soft float"); |
| | 3887 | }, |
| | 3888 | 32 => blk: { |
| | 3889 | const lhs_val = lhs.toFloat(f32); |
| | 3890 | const rhs_val = rhs.toFloat(f32); |
| | 3891 | const val_payload = try allocator.create(Value.Payload.Float_32); |
| | 3892 | val_payload.* = .{ .val = lhs_val + rhs_val }; |
| | 3893 | break :blk &val_payload.base; |
| | 3894 | }, |
| | 3895 | 64 => blk: { |
| | 3896 | const lhs_val = lhs.toFloat(f64); |
| | 3897 | const rhs_val = rhs.toFloat(f64); |
| | 3898 | const val_payload = try allocator.create(Value.Payload.Float_64); |
| | 3899 | val_payload.* = .{ .val = lhs_val + rhs_val }; |
| | 3900 | break :blk &val_payload.base; |
| | 3901 | }, |
| | 3902 | 128 => blk: { |
| | 3903 | @panic("TODO Big float"); |
| | 3904 | }, |
| | 3905 | else => unreachable, |
| | 3906 | }; |
| | 3907 | |
| | 3908 | return Value.initPayload(val_payload); |
| | 3909 | } |
| | 3910 | |
| | 3911 | fn floatSub(cur_target: Target, allocator: *Allocator, float_type: Type, lhs: Value, rhs: Value) !Value { |
| | 3912 | var bit_count = switch (float_type.tag()) { |
| | 3913 | .comptime_float => 128, |
| | 3914 | else => float_type.floatBits(cur_target), |
| | 3915 | }; |
| | 3916 | |
| | 3917 | const val_payload = switch (bit_count) { |
| | 3918 | 16 => { |
| | 3919 | @panic("TODO soft float"); |
| | 3920 | }, |
| | 3921 | 32 => blk: { |
| | 3922 | const lhs_val = lhs.toFloat(f32); |
| | 3923 | const rhs_val = rhs.toFloat(f32); |
| | 3924 | const val_payload = try allocator.create(Value.Payload.Float_32); |
| | 3925 | val_payload.* = .{ .val = lhs_val - rhs_val }; |
| | 3926 | break :blk &val_payload.base; |
| | 3927 | }, |
| | 3928 | 64 => blk: { |
| | 3929 | const lhs_val = lhs.toFloat(f64); |
| | 3930 | const rhs_val = rhs.toFloat(f64); |
| | 3931 | const val_payload = try allocator.create(Value.Payload.Float_64); |
| | 3932 | val_payload.* = .{ .val = lhs_val - rhs_val }; |
| | 3933 | break :blk &val_payload.base; |
| | 3934 | }, |
| | 3935 | 128 => blk: { |
| | 3936 | @panic("TODO Big float"); |
| | 3937 | }, |
| | 3938 | else => unreachable, |
| | 3939 | }; |
| | 3940 | |
| | 3941 | return Value.initPayload(val_payload); |
| | 3942 | } |