| ... | @@ -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 | } |
| 3039 | | 3038 | |
| 3040 | fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 3039 | fn 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 | } |
| 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 | const tracy = trace(@src()); | 3048 | const tracy = trace(@src()); |
| 3046 | defer tracy.end(); | 3049 | defer tracy.end(); |
| 3047 | | 3050 | |
| 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); |
| 3050 | | 3053 | |
| 3051 | if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and | 3054 | 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 | } |
| 3057 | | 3080 | |
| 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 | }; | | |
| 3083 | | 3083 | |
| 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 | } |
| 3090 | | 3093 | |
| 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 |
| | 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 | 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 { |
| ... | @@ -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 | } |
| 3516 | | 3574 | |
| 3517 | // TODO error notes pointing out each type | 3575 | // 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 { |
| 3763 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | 3821 | fn 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 | |
| | 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 | } |