| ... | @@ -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,10 +3036,15 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn | ... | @@ -3037,10 +3036,15 @@ 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 { |
| | 3040 | // extend this swich as additional operators are implemented |
| | 3041 | return switch (tag) { |
| | 3042 | .add, .sub => true, |
| | 3043 | else => false, |
| | 3044 | }; |
| 3041 | } | 3045 | } |
| 3042 | | 3046 | |
| 3043 | 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 { |
| 3044 | const tracy = trace(@src()); | 3048 | const tracy = trace(@src()); |
| 3045 | defer tracy.end(); | 3049 | defer tracy.end(); |
| 3046 | | 3050 | |
| ... | @@ -3049,80 +3053,118 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerErro | ... | @@ -3049,80 +3053,118 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerErro |
| 3049 | | 3053 | |
| 3050 | const instructions = &[_]*Inst{ lhs, rhs }; | 3054 | const instructions = &[_]*Inst{ lhs, rhs }; |
| 3051 | const resolved_type = try self.resolvePeerTypes(scope, instructions); | 3055 | const resolved_type = try self.resolvePeerTypes(scope, instructions); |
| 3052 | const resolved_tag = resolved_type.zigTypeTag(); | | |
| 3053 | | 3056 | |
| 3054 | const is_int = resolved_tag == .Int or resolved_tag == .ComptimeInt; | 3057 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) |
| | 3058 | resolved_type.elemType() |
| | 3059 | else |
| | 3060 | resolved_type; |
| | 3061 | |
| | 3062 | const scalar_tag = scalar_type.zigTypeTag(); |
| | 3063 | |
| | 3064 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { |
| | 3065 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { |
| | 3066 | return self.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ |
| | 3067 | lhs.ty.arrayLen(), |
| | 3068 | rhs.ty.arrayLen(), |
| | 3069 | }); |
| | 3070 | } |
| | 3071 | return self.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); |
| | 3072 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { |
| | 3073 | return self.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ |
| | 3074 | lhs.ty, |
| | 3075 | rhs.ty, |
| | 3076 | }); |
| | 3077 | } |
| | 3078 | |
| | 3079 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| | 3080 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; |
| 3055 | | 3081 | |
| 3056 | if (!is_int) { | 3082 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { |
| 3057 | return self.fail(scope, inst.base.src, "TODO analyze arithmetic for types {} and {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() }); | 3083 | return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); |
| 3058 | } | 3084 | } |
| 3059 | | 3085 | |
| 3060 | if (lhs.value()) |lhs_val| { | 3086 | if (lhs.value()) |lhs_val| { |
| 3061 | if (rhs.value()) |rhs_val| { | 3087 | if (rhs.value()) |rhs_val| { |
| 3062 | return self.analyzeInstMath(scope, resolved_type, &inst.base, lhs_val, rhs_val); | 3088 | return self.analyzeInstScalar(scope, scalar_type, inst, lhs_val, rhs_val); |
| 3063 | } | 3089 | } |
| 3064 | } | 3090 | } |
| 3065 | | 3091 | |
| 3066 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 3092 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 3067 | | 3093 | const ir_tag = switch (inst.base.tag) { |
| 3068 | return switch (inst.base.tag) { | 3094 | .add => Inst.Tag.add, |
| 3069 | .add => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Add, .{ | 3095 | .sub => Inst.Tag.sub, |
| 3070 | .lhs = lhs, | 3096 | else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), |
| 3071 | .rhs = rhs, | | |
| 3072 | }), | | |
| 3073 | .sub => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Sub, .{ | | |
| 3074 | .lhs = lhs, | | |
| 3075 | .rhs = rhs, | | |
| 3076 | }), | | |
| 3077 | else => self.fail(scope, inst.base.src, "TODO Implement arithmetic for operand {}", .{@tagName(inst.base.tag)}), | | |
| 3078 | }; | 3097 | }; |
| | 3098 | |
| | 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); |
| 3079 | } | 3119 | } |
| 3080 | | 3120 | |
| 3081 | /// Analyzes operands that are known at comptime | 3121 | /// Analyzes operands that are known at comptime |
| 3082 | fn analyzeInstMath(self: *Module, scope: *Scope, res_type: Type, base: *zir.Inst, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | 3122 | fn analyzeInstScalar(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { |
| 3083 | // incase rhs is 0, simply return lhs without doing any calculations | 3123 | // incase rhs is 0, simply return lhs without doing any calculations |
| 3084 | // TODO Once division is implemented we should throw an error when dividing by 0. | 3124 | // TODO Once division is implemented we should throw an error when dividing by 0. |
| 3085 | if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { | 3125 | if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { |
| 3086 | return self.constInst(scope, base.src, .{ | 3126 | return self.constInst(scope, inst.base.src, .{ |
| 3087 | .ty = res_type, | 3127 | .ty = res_type, |
| 3088 | .val = lhs_val, | 3128 | .val = lhs_val, |
| 3089 | }); | 3129 | }); |
| 3090 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 3130 | } |
| 3091 | return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs); | | |
| 3092 | } | | |
| 3093 | | | |
| 3094 | // TODO is this a performance issue? maybe we should try the operation without | | |
| 3095 | // resorting to BigInt first. | | |
| 3096 | var lhs_space: Value.BigIntSpace = undefined; | | |
| 3097 | var rhs_space: Value.BigIntSpace = undefined; | | |
| 3098 | const lhs_bigint = lhs_val.toBigInt(&lhs_space); | | |
| 3099 | const rhs_bigint = rhs_val.toBigInt(&rhs_space); | | |
| 3100 | const limbs = try scope.arena().alloc( | | |
| 3101 | std.math.big.Limb, | | |
| 3102 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | | |
| 3103 | ); | | |
| 3104 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | | |
| 3105 | switch (base.tag) { | | |
| 3106 | .add => result_bigint.add(lhs_bigint, rhs_bigint), | | |
| 3107 | .sub => result_bigint.sub(lhs_bigint, rhs_bigint), | | |
| 3108 | else => return error.AnalysisFail, | | |
| 3109 | } | | |
| 3110 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | | |
| 3111 | | | |
| 3112 | const val_payload = if (result_bigint.positive) blk: { | | |
| 3113 | const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); | | |
| 3114 | val_payload.* = .{ .limbs = result_limbs }; | | |
| 3115 | break :blk &val_payload.base; | | |
| 3116 | } else blk: { | | |
| 3117 | const val_payload = try scope.arena().create(Value.Payload.IntBigNegative); | | |
| 3118 | val_payload.* = .{ .limbs = result_limbs }; | | |
| 3119 | break :blk &val_payload.base; | | |
| 3120 | }; | | |
| 3121 | | 3131 | |
| 3122 | return self.constInst(scope, base.src, .{ | 3132 | if (lhs_val.isFloat() or res_type.tag() == .comptime_float) { |
| 3123 | .ty = res_type, | 3133 | return self.fail(scope, inst.base.src, "TODO implement arithmetic for floats", .{}); |
| 3124 | .val = Value.initPayload(val_payload), | 3134 | } else { |
| 3125 | }); | 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 | }; |
| | 3162 | |
| | 3163 | return self.constInst(scope, inst.base.src, .{ |
| | 3164 | .ty = res_type, |
| | 3165 | .val = Value.initPayload(val_payload), |
| | 3166 | }); |
| | 3167 | } |
| 3126 | } | 3168 | } |
| 3127 | | 3169 | |
| 3128 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { | 3170 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { |