| ... | ... | @@ -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,10 +3036,15 @@ 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 { |
| 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 | 3048 | const tracy = trace(@src()); |
| 3045 | 3049 | defer tracy.end(); |
| 3046 | 3050 | |
| ... | ... | @@ -3049,80 +3053,118 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerErro |
| 3049 | 3053 | |
| 3050 | 3054 | const instructions = &[_]*Inst{ lhs, rhs }; |
| 3051 | 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) { |
| 3057 | | return self.fail(scope, inst.base.src, "TODO analyze arithmetic for types {} and {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() }); |
| 3082 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { |
| 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 | 3086 | if (lhs.value()) |lhs_val| { |
| 3061 | 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 | 3092 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 3067 | | |
| 3068 | | return switch (inst.base.tag) { |
| 3069 | | .add => self.addNewInstArgs(b, inst.base.src, resolved_type, Inst.Add, .{ |
| 3070 | | .lhs = lhs, |
| 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)}), |
| 3093 | const ir_tag = switch (inst.base.tag) { |
| 3094 | .add => Inst.Tag.add, |
| 3095 | .sub => Inst.Tag.sub, |
| 3096 | else => return 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 | 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 | 3123 | // incase rhs is 0, simply return lhs without doing any calculations |
| 3084 | 3124 | // TODO Once division is implemented we should throw an error when dividing by 0. |
| 3085 | 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 | 3127 | .ty = res_type, |
| 3088 | 3128 | .val = lhs_val, |
| 3089 | 3129 | }); |
| 3090 | | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 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 | | }; |
| 3130 | } |
| 3121 | 3131 | |
| 3122 | | return self.constInst(scope, base.src, .{ |
| 3123 | | .ty = res_type, |
| 3124 | | .val = Value.initPayload(val_payload), |
| 3125 | | }); |
| 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 | }; |
| 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 | 3170 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { |