| ... | ... | @@ -2400,8 +2400,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2400 | 2400 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), |
| 2401 | 2401 | .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), |
| 2402 | 2402 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), |
| 2403 | | .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), |
| 2404 | | .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), |
| 2403 | .add, .sub => return self.analyzeInstArithmetic(scope, old_inst.cast(zir.Inst.BinOp).?), |
| 2405 | 2404 | .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt), |
| 2406 | 2405 | .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte), |
| 2407 | 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 | 3036 | return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{}); |
| 3038 | 3037 | } |
| 3039 | 3038 | |
| 3040 | | fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 3041 | | return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{}); |
| 3039 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { |
| 3040 | // extend this swich as additional operators are implemented |
| 3041 | return switch (tag) { |
| 3042 | .add, .sub => true, |
| 3043 | else => false, |
| 3044 | }; |
| 3042 | 3045 | } |
| 3043 | 3046 | |
| 3044 | | fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 3047 | fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 3045 | 3048 | const tracy = trace(@src()); |
| 3046 | 3049 | defer tracy.end(); |
| 3047 | 3050 | |
| 3048 | 3051 | const lhs = try self.resolveInst(scope, inst.positionals.lhs); |
| 3049 | 3052 | const rhs = try self.resolveInst(scope, inst.positionals.rhs); |
| 3050 | 3053 | |
| 3051 | | if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and |
| 3052 | | (rhs.ty.zigTypeTag() == .Int or rhs.ty.zigTypeTag() == .ComptimeInt)) |
| 3053 | | { |
| 3054 | | if (!lhs.ty.eql(rhs.ty)) { |
| 3055 | | return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{}); |
| 3054 | const instructions = &[_]*Inst{ lhs, rhs }; |
| 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); |
| 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 | } |
| 3057 | 3080 | |
| 3058 | | if (lhs.value()) |lhs_val| { |
| 3059 | | if (rhs.value()) |rhs_val| { |
| 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 | | }; |
| 3081 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 3082 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; |
| 3083 | 3083 | |
| 3084 | | return self.constInst(scope, inst.base.src, .{ |
| 3085 | | .ty = lhs.ty, |
| 3086 | | .val = Value.initPayload(val_payload), |
| 3087 | | }); |
| 3088 | | } |
| 3084 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { |
| 3085 | return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); |
| 3086 | } |
| 3087 | |
| 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 | } |
| 3090 | 3093 | |
| 3091 | | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 3092 | | return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs); |
| 3094 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 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 |
| 3105 | fn 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 | } |
| 3096 | 3139 | |
| 3097 | 3140 | fn 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 | 3556 | prev_inst = next_inst; |
| 3514 | 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 | } |
| 3516 | 3574 | |
| 3517 | 3575 | // TODO error notes pointing out each type |
| 3518 | 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 | 3821 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3764 | 3822 | return @bitCast(u128, a) == @bitCast(u128, b); |
| 3765 | 3823 | } |
| 3824 | |
| 3825 | fn 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 | |
| 3853 | fn 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 | |
| 3881 | fn 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 | |
| 3915 | fn 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 | } |