| ... | ... | @@ -3053,6 +3053,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In |
| 3053 | 3053 | |
| 3054 | 3054 | const instructions = &[_]*Inst{ lhs, rhs }; |
| 3055 | 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 | 3059 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) |
| 3058 | 3060 | resolved_type.elemType() |
| ... | ... | @@ -3083,8 +3085,8 @@ fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In |
| 3083 | 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| { |
| 3087 | | if (rhs.value()) |rhs_val| { |
| 3088 | if (casted_lhs.value()) |lhs_val| { |
| 3089 | if (casted_rhs.value()) |rhs_val| { |
| 3088 | 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 | 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) { |
| 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); |
| 3101 | return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); |
| 3119 | 3102 | } |
| 3120 | 3103 | |
| 3121 | 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 | 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) { |
| 3133 | | return self.fail(scope, inst.base.src, "TODO implement arithmetic for floats", .{}); |
| 3134 | | } else { |
| 3135 | | // TODO is this a performance issue? maybe we should try the operation without |
| 3136 | | // resorting to BigInt first. |
| 3137 | | var lhs_space: Value.BigIntSpace = undefined; |
| 3138 | | var rhs_space: Value.BigIntSpace = undefined; |
| 3139 | | const lhs_bigint = lhs_val.toBigInt(&lhs_space); |
| 3140 | | const rhs_bigint = rhs_val.toBigInt(&rhs_space); |
| 3141 | | const limbs = try scope.arena().alloc( |
| 3142 | | std.math.big.Limb, |
| 3143 | | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, |
| 3144 | | ); |
| 3145 | | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 3146 | | switch (inst.base.tag) { |
| 3147 | | .add => result_bigint.add(lhs_bigint, rhs_bigint), |
| 3148 | | .sub => result_bigint.sub(lhs_bigint, rhs_bigint), |
| 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 | | }; |
| 3116 | const value = try switch (inst.base.tag) { |
| 3117 | .add => blk: { |
| 3118 | const val = if (is_int) |
| 3119 | bigIntAdd(scope.arena(), lhs_val, rhs_val) |
| 3120 | else |
| 3121 | floatAdd(self.target(), scope.arena(), res_type, lhs_val, rhs_val); |
| 3122 | break :blk val; |
| 3123 | }, |
| 3124 | .sub => blk: { |
| 3125 | const val = if (is_int) |
| 3126 | bigIntSub(scope.arena(), lhs_val, rhs_val) |
| 3127 | else |
| 3128 | floatSub(self.target(), scope.arena(), res_type, 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 | }; |
| 3162 | 3133 | |
| 3163 | | return self.constInst(scope, inst.base.src, .{ |
| 3164 | | .ty = res_type, |
| 3165 | | .val = Value.initPayload(val_payload), |
| 3166 | | }); |
| 3167 | | } |
| 3134 | return self.constInst(scope, inst.base.src, .{ |
| 3135 | .ty = res_type, |
| 3136 | .val = value, |
| 3137 | }); |
| 3168 | 3138 | } |
| 3169 | 3139 | |
| 3170 | 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 | 3556 | prev_inst = next_inst; |
| 3587 | 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 | 3572 | // TODO error notes pointing out each type |
| 3591 | 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 | 3818 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3837 | 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 | } |