已合并
Revert "!4561 Range Analysis of MUL and MOD" #4583
AtomGit-Bot创建于 2023年8月4日
Revert "!4561 Range Analysis of MUL and MOD" #4583
已合并
从refs/pull/4583/head合入到master
共 8 个文件变更+14-158
| @@ -102,10 +102,9 @@ public: | |||
| 102 | 102 | ||
| 103 | static constexpr int32_t UINT30_MAX = 0x3fffffff; | 103 | static constexpr int32_t UINT30_MAX = 0x3fffffff; |
| 104 | static constexpr int32_t TYPED_ARRAY_ONHEAP_MAX = JSTypedArray::MAX_ONHEAP_LENGTH; | 104 | static constexpr int32_t TYPED_ARRAY_ONHEAP_MAX = JSTypedArray::MAX_ONHEAP_LENGTH; |
| 105 | - static constexpr int32_t UINT18_MAX = (1 << 18) - 1; | 105 | + static const inline std::vector<int32_t> rangeBounds_ = { INT32_MIN, INT32_MIN + 1, |
| 106 | - static const inline std::vector<int32_t> rangeBounds_ = { INT32_MIN, INT32_MIN + 1, -UINT18_MAX, -TYPED_ARRAY_ONHEAP_MAX, | ||
| 107 | -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1, TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, | 106 | -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1, TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, |
| 108 | - TYPED_ARRAY_ONHEAP_MAX * 3, UINT18_MAX, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX }; | 107 | + TYPED_ARRAY_ONHEAP_MAX * 3, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX }; |
| 109 | 108 | ||
| 110 | static RangeInfo NONE() | 109 | static RangeInfo NONE() |
| 111 | { | 110 | { |
| @@ -154,81 +153,11 @@ public: | |||
| 154 | 153 | ||
| 155 | RangeInfo operator+ (const RangeInfo &rhs) const | 154 | RangeInfo operator+ (const RangeInfo &rhs) const |
| 156 | { | 155 | { |
| 157 | - ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 158 | int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_; | 156 | int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_; |
| 159 | int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_; | 157 | int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_; |
| 160 | return RangeInfo(nmin, nmax); | 158 | return RangeInfo(nmin, nmax); |
| 161 | } | 159 | } |
| 162 | 160 | ||
| 163 | - RangeInfo operator% (const RangeInfo &rhs) const | ||
| 164 | - { | ||
| 165 | - ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 166 | - RangeInfo result = RangeInfo(0, 0); | ||
| 167 | - int32_t nmax = std::max(std::abs(rhs.min_), std::abs(rhs.max_)); | ||
| 168 | - if (max_ > 0) result = result.Union(RangeInfo(0, nmax - 1)); | ||
| 169 | - if (min_ < 0) result = result.Union(RangeInfo(-nmax + 1, 0)); | ||
| 170 | - return result; | ||
| 171 | - } | ||
| 172 | - | ||
| 173 | - bool MaybeZero() const | ||
| 174 | - { | ||
| 175 | - return min_ <= 0 && max_ >= 0; | ||
| 176 | - } | ||
| 177 | - | ||
| 178 | - RangeInfo operator* (const RangeInfo &rhs) const | ||
| 179 | - { | ||
| 180 | - ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 181 | - int32_t nmax = GetMaxMulResult(rhs); | ||
| 182 | - int32_t nmin = GetMinMulResult(rhs); | ||
| 183 | - return RangeInfo(nmin, nmax); | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - int32_t GetMaxMulResult(const RangeInfo &rhs) const | ||
| 187 | - { | ||
| 188 | - return std::max({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_), TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) }); | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - int32_t GetMinMulResult(const RangeInfo &rhs) const | ||
| 192 | - { | ||
| 193 | - return std::min({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_), TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) }); | ||
| 194 | - } | ||
| 195 | - | ||
| 196 | - int32_t TryMul(int32_t lhs, int32_t rhs) const | ||
| 197 | - { | ||
| 198 | - if (MaybeMulOverflow(lhs, rhs)){ | ||
| 199 | - return INT32_MAX; | ||
| 200 | - } | ||
| 201 | - if (MaybeMulUnderflow(lhs, rhs)){ | ||
| 202 | - return INT32_MIN; | ||
| 203 | - } | ||
| 204 | - return lhs * rhs; | ||
| 205 | - } | ||
| 206 | - | ||
| 207 | - bool MaybeMulOverflowOrUnderflow(const RangeInfo &rhs) const | ||
| 208 | - { | ||
| 209 | - return MaybeMulOverflow(rhs) || MaybeMulUnderflow(rhs); | ||
| 210 | - } | ||
| 211 | - | ||
| 212 | - bool MaybeMulUnderflow(const RangeInfo &rhs) const | ||
| 213 | - { | ||
| 214 | - return MaybeMulUnderflow(min_, rhs.max_) || MaybeMulUnderflow(max_, rhs.min_); | ||
| 215 | - } | ||
| 216 | - | ||
| 217 | - bool MaybeMulOverflow(const RangeInfo &rhs) const | ||
| 218 | - { | ||
| 219 | - return MaybeMulOverflow(max_, rhs.max_) || MaybeMulOverflow(min_, rhs.min_); | ||
| 220 | - } | ||
| 221 | - | ||
| 222 | - bool MaybeMulUnderflow(int32_t lhs, int32_t rhs) const | ||
| 223 | - { | ||
| 224 | - return (lhs > 0 && rhs < 0 && rhs < INT32_MIN / lhs) || (lhs < 0 && rhs > 0 && lhs < INT32_MIN / rhs); | ||
| 225 | - } | ||
| 226 | - | ||
| 227 | - bool MaybeMulOverflow(int32_t lhs, int32_t rhs) const | ||
| 228 | - { | ||
| 229 | - return (lhs > 0 && rhs > 0 && lhs > INT32_MAX / rhs) || (lhs < 0 && rhs < 0 && lhs < INT32_MAX / rhs); | ||
| 230 | - } | ||
| 231 | - | ||
| 232 | bool MaybeSubOverflow(const RangeInfo &rhs) const | 161 | bool MaybeSubOverflow(const RangeInfo &rhs) const |
| 233 | { | 162 | { |
| 234 | return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_); | 163 | return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_); |
| @@ -246,7 +175,6 @@ public: | |||
| 246 | 175 | ||
| 247 | RangeInfo operator- (const RangeInfo &rhs) const | 176 | RangeInfo operator- (const RangeInfo &rhs) const |
| 248 | { | 177 | { |
| 249 | - ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 250 | int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_; | 178 | int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_; |
| 251 | int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_; | 179 | int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_; |
| 252 | return RangeInfo(nmin, nmax); | 180 | return RangeInfo(nmin, nmax); |
| @@ -262,8 +190,6 @@ public: | |||
| 262 | 190 | ||
| 263 | RangeInfo SHR(const RangeInfo &rhs) const | 191 | RangeInfo SHR(const RangeInfo &rhs) const |
| 264 | { | 192 | { |
| 265 | - ASSERT(min_ <= max_); | ||
| 266 | - ASSERT(rhs.max_ == rhs.min_); | ||
| 267 | if (MaybeShrOverflow(rhs)) { | 193 | if (MaybeShrOverflow(rhs)) { |
| 268 | // assume no overflow occurs since overflow will lead to deopt | 194 | // assume no overflow occurs since overflow will lead to deopt |
| 269 | return RangeInfo(0, std::max(0, GetMax())); | 195 | return RangeInfo(0, std::max(0, GetMax())); |
| @@ -278,7 +204,6 @@ public: | |||
| 278 | 204 | ||
| 279 | RangeInfo ASHR(const RangeInfo &rhs) const | 205 | RangeInfo ASHR(const RangeInfo &rhs) const |
| 280 | { | 206 | { |
| 281 | - ASSERT(min_ <= max_); | ||
| 282 | ASSERT(rhs.max_ == rhs.min_); | 207 | ASSERT(rhs.max_ == rhs.min_); |
| 283 | int32_t shift = rhs.max_ & 0x1f; // 0x1f : shift bits | 208 | int32_t shift = rhs.max_ & 0x1f; // 0x1f : shift bits |
| 284 | int32_t nmin = min_ >> shift; | 209 | int32_t nmin = min_ >> shift; |
| @@ -355,9 +355,7 @@ void NumberSpeculativeLowering::VisitNumberMod(GateRef gate) | |||
| 355 | } | 355 | } |
| 356 | GateRef result = Circuit::NullGate(); | 356 | GateRef result = Circuit::NullGate(); |
| 357 | if (gateType.IsIntType()) { | 357 | if (gateType.IsIntType()) { |
| 358 | - if(GetRange(right).MaybeZero()) { | 358 | + builder_.Int32CheckRightIsZero(right); |
| 359 | - builder_.Int32CheckRightIsZero(right); | ||
| 360 | - } | ||
| 361 | result = CalculateInts<Op>(left, right); | 359 | result = CalculateInts<Op>(left, right); |
| 362 | UpdateRange(result, GetRange(gate)); | 360 | UpdateRange(result, GetRange(gate)); |
| 363 | acc_.SetMachineType(gate, MachineType::I32); | 361 | acc_.SetMachineType(gate, MachineType::I32); |
| @@ -578,9 +576,6 @@ GateRef NumberSpeculativeLowering::CalculateInts(GateRef left, GateRef right) | |||
| 578 | break; | 576 | break; |
| 579 | } | 577 | } |
| 580 | case TypedBinOp::TYPED_MUL: | 578 | case TypedBinOp::TYPED_MUL: |
| 581 | - if(!leftRange.MaybeMulOverflowOrUnderflow(rightRange)) { | ||
| 582 | - return builder_.Int32Mul(left, right); | ||
| 583 | - } | ||
| 584 | res = builder_.MulWithOverflow(left, right); | 579 | res = builder_.MulWithOverflow(left, right); |
| 585 | break; | 580 | break; |
| 586 | case TypedBinOp::TYPED_MOD: { | 581 | case TypedBinOp::TYPED_MOD: { |
| @@ -139,12 +139,6 @@ GateRef RangeAnalysis::VisitTypedBinaryOp(GateRef gate) | |||
| 139 | case TypedBinOp::TYPED_SUB: | 139 | case TypedBinOp::TYPED_SUB: |
| 140 | range = GetRangeOfCalculate<TypedBinOp::TYPED_SUB>(gate); | 140 | range = GetRangeOfCalculate<TypedBinOp::TYPED_SUB>(gate); |
| 141 | break; | 141 | break; |
| 142 | - case TypedBinOp::TYPED_MOD: | ||
| 143 | - range = GetRangeOfCalculate<TypedBinOp::TYPED_MOD>(gate); | ||
| 144 | - break; | ||
| 145 | - case TypedBinOp::TYPED_MUL: | ||
| 146 | - range = GetRangeOfCalculate<TypedBinOp::TYPED_MUL>(gate); | ||
| 147 | - break; | ||
| 148 | case TypedBinOp::TYPED_SHR: | 142 | case TypedBinOp::TYPED_SHR: |
| 149 | range = GetRangeOfShift<TypedBinOp::TYPED_SHR>(gate); | 143 | range = GetRangeOfShift<TypedBinOp::TYPED_SHR>(gate); |
| 150 | break; | 144 | break; |
| @@ -187,6 +181,7 @@ GateRef RangeAnalysis::VisitRangeGuard(GateRef gate) | |||
| 187 | template<TypedBinOp Op> | 181 | template<TypedBinOp Op> |
| 188 | RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) | 182 | RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) |
| 189 | { | 183 | { |
| 184 | + ASSERT((Op == TypedBinOp::TYPED_ADD) || (Op == TypedBinOp::TYPED_SUB)); | ||
| 190 | auto left = GetRange(acc_.GetValueIn(gate, 0)); | 185 | auto left = GetRange(acc_.GetValueIn(gate, 0)); |
| 191 | auto right = GetRange(acc_.GetValueIn(gate, 1)); | 186 | auto right = GetRange(acc_.GetValueIn(gate, 1)); |
| 192 | if (left.IsNone() || right.IsNone()) { | 187 | if (left.IsNone() || right.IsNone()) { |
| @@ -197,10 +192,6 @@ RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) | |||
| 197 | return left + right; | 192 | return left + right; |
| 198 | case TypedBinOp::TYPED_SUB: | 193 | case TypedBinOp::TYPED_SUB: |
| 199 | return left - right; | 194 | return left - right; |
| 200 | - case TypedBinOp::TYPED_MOD: | ||
| 201 | - return left % right; | ||
| 202 | - case TypedBinOp::TYPED_MUL: | ||
| 203 | - return left * right; | ||
| 204 | default: | 195 | default: |
| 205 | return RangeInfo::ANY(); | 196 | return RangeInfo::ANY(); |
| 206 | } | 197 | } |
| @@ -330,12 +321,6 @@ void RangeAnalysis::PrintRangeInfo() const | |||
| 330 | case TypedBinOp::TYPED_ASHR: | 321 | case TypedBinOp::TYPED_ASHR: |
| 331 | log += " ashr"; | 322 | log += " ashr"; |
| 332 | break; | 323 | break; |
| 333 | - case TypedBinOp::TYPED_MOD: | ||
| 334 | - log += " mod"; | ||
| 335 | - break; | ||
| 336 | - case TypedBinOp::TYPED_MUL: | ||
| 337 | - log += " mul"; | ||
| 338 | - break; | ||
| 339 | default: | 324 | default: |
| 340 | log += " other"; | 325 | log += " other"; |
| 341 | break; | 326 | break; |
| @@ -14,6 +14,7 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -205,6 +206,10 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 205 | switch (bytecodeInfo.GetOpcode()) { | 206 | switch (bytecodeInfo.GetOpcode()) { |
| 206 | case EcmaOpcode::LDNAN: | 207 | case EcmaOpcode::LDNAN: |
| 207 | case EcmaOpcode::LDINFINITY: | 208 | case EcmaOpcode::LDINFINITY: |
| 209 | + case EcmaOpcode::MOD2_IMM8_V8: | ||
| 210 | + case EcmaOpcode::AND2_IMM8_V8: | ||
| 211 | + case EcmaOpcode::OR2_IMM8_V8: | ||
| 212 | + case EcmaOpcode::XOR2_IMM8_V8: | ||
| 208 | case EcmaOpcode::TONUMBER_IMM8: | 213 | case EcmaOpcode::TONUMBER_IMM8: |
| 209 | case EcmaOpcode::NEG_IMM8: | 214 | case EcmaOpcode::NEG_IMM8: |
| 210 | case EcmaOpcode::EXP_IMM8_V8: | 215 | case EcmaOpcode::EXP_IMM8_V8: |
| @@ -214,9 +219,6 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 214 | case EcmaOpcode::ASHR2_IMM8_V8: | 219 | case EcmaOpcode::ASHR2_IMM8_V8: |
| 215 | case EcmaOpcode::SHR2_IMM8_V8: | 220 | case EcmaOpcode::SHR2_IMM8_V8: |
| 216 | case EcmaOpcode::NOT_IMM8: | 221 | case EcmaOpcode::NOT_IMM8: |
| 217 | - case EcmaOpcode::AND2_IMM8_V8: | ||
| 218 | - case EcmaOpcode::OR2_IMM8_V8: | ||
| 219 | - case EcmaOpcode::XOR2_IMM8_V8: | ||
| 220 | return SetIntType(gate); | 222 | return SetIntType(gate); |
| 221 | case EcmaOpcode::LDBIGINT_ID16: | 223 | case EcmaOpcode::LDBIGINT_ID16: |
| 222 | return SetBigIntType(gate); | 224 | return SetBigIntType(gate); |
| @@ -259,8 +261,6 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 259 | return InferSub2(gate); | 261 | return InferSub2(gate); |
| 260 | case EcmaOpcode::MUL2_IMM8_V8: | 262 | case EcmaOpcode::MUL2_IMM8_V8: |
| 261 | return InferMul2(gate); | 263 | return InferMul2(gate); |
| 262 | - case EcmaOpcode::MOD2_IMM8_V8: | ||
| 263 | - return InferMod2(gate); | ||
| 264 | case EcmaOpcode::DIV2_IMM8_V8: | 264 | case EcmaOpcode::DIV2_IMM8_V8: |
| 265 | return InferDiv2(gate); | 265 | return InferDiv2(gate); |
| 266 | case EcmaOpcode::INC_IMM8: | 266 | case EcmaOpcode::INC_IMM8: |
| @@ -553,31 +553,6 @@ bool MethodTypeInfer::InferMul2(GateRef gate) | |||
| 553 | return UpdateType(gate, GateType::NumberType()); | 553 | return UpdateType(gate, GateType::NumberType()); |
| 554 | } | 554 | } |
| 555 | 555 | ||
| 556 | -/* | ||
| 557 | - * Type Infer rule(satisfy commutative law): | ||
| 558 | - * number % number = number | ||
| 559 | - * int % number = number | ||
| 560 | - * double % number = double | ||
| 561 | - * int % int = int | ||
| 562 | - * int % double = double | ||
| 563 | - * double % double = double | ||
| 564 | - */ | ||
| 565 | -bool MethodTypeInfer::InferMod2(GateRef gate) | ||
| 566 | -{ | ||
| 567 | - // 2: number of value inputs | ||
| 568 | - ASSERT(gateAccessor_.GetNumValueIn(gate) == 2); | ||
| 569 | - auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0)); | ||
| 570 | - auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1)); | ||
| 571 | - if ((firInType.IsNumberType() && secInType.IsDoubleType()) || | ||
| 572 | - (firInType.IsDoubleType() && secInType.IsNumberType())) { | ||
| 573 | - return UpdateType(gate, GateType::DoubleType()); | ||
| 574 | - } | ||
| 575 | - if ((firInType.IsIntType() && secInType.IsIntType())) { | ||
| 576 | - return UpdateType(gate, GateType::IntType()); | ||
| 577 | - } | ||
| 578 | - return UpdateType(gate, GateType::NumberType()); | ||
| 579 | -} | ||
| 580 | - | ||
| 581 | /* | 556 | /* |
| 582 | * Type Infer rule(satisfy commutative law): | 557 | * Type Infer rule(satisfy commutative law): |
| 583 | * in type lowering, both elements will be changed to float64 firstly. | 558 | * in type lowering, both elements will be changed to float64 firstly. |
| @@ -84,7 +84,6 @@ private: | |||
| 84 | bool InferAdd2(GateRef gate); | 84 | bool InferAdd2(GateRef gate); |
| 85 | bool InferSub2(GateRef gate); | 85 | bool InferSub2(GateRef gate); |
| 86 | bool InferMul2(GateRef gate); | 86 | bool InferMul2(GateRef gate); |
| 87 | - bool InferMod2(GateRef gate); | ||
| 88 | bool InferDiv2(GateRef gate); | 87 | bool InferDiv2(GateRef gate); |
| 89 | bool InferIncDec(GateRef gate); | 88 | bool InferIncDec(GateRef gate); |
| 90 | bool InferToNumberic(GateRef gate); | 89 | bool InferToNumberic(GateRef gate); |
| @@ -68,26 +68,6 @@ function testcase6() { | |||
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | -//mod CheckRightIsZero | ||
| 72 | -function testcase7() { | ||
| 73 | - const a: number= 20; | ||
| 74 | - let b: number = 5 % a;// not gen CheckRightIsZero | ||
| 75 | - let c: number = 5 % b;// gen CheckRightIsZero | ||
| 76 | -} | ||
| 77 | - | ||
| 78 | -//mul_with_overcheck | ||
| 79 | -function testcase8(a:number, x:number) { | ||
| 80 | - let z = a >>> 1; | ||
| 81 | - let b: number = z % 10;//range[0, 4095] | ||
| 82 | - | ||
| 83 | - let c: number = 10 * b;//not gen mul_with_overcheck, range[0,262143] | ||
| 84 | - let d: number = c * b;//not gen mul_with_overcheck, range[0,1073741823] | ||
| 85 | - let e: number = d * c;//gen mul_with_overcheck | ||
| 86 | - | ||
| 87 | - x = x >>> 1; | ||
| 88 | - let h: number = x * x;//gen mul_with_overcheck | ||
| 89 | -} | ||
| 90 | - | ||
| 91 | testcase1(); // 8 | 71 | testcase1(); // 8 |
| 92 | testcase2(); // 8 | 72 | testcase2(); // 8 |
| 93 | 73 | ||
| @@ -95,7 +75,4 @@ testcase3(); | |||
| 95 | testcase4(); | 75 | testcase4(); |
| 96 | 76 | ||
| 97 | testcase5(); | 77 | testcase5(); |
| 98 | -testcase6(); | 78 | +testcase6(); |
| 99 | - | ||
| 100 | -testcase7(); | ||
| 101 | -testcase8(4, 107341001);//deopt | ||
| @@ -18,13 +18,13 @@ declare function AssertType(value:any, type:string):void; | |||
| 18 | let x:number = 123; | 18 | let x:number = 123; |
| 19 | let y:number = 2; | 19 | let y:number = 2; |
| 20 | let andRes = x & y; | 20 | let andRes = x & y; |
| 21 | - AssertType(andRes, "int"); | 21 | + AssertType(andRes, "number"); |
| 22 | 22 | ||
| 23 | let orRes = x | y; | 23 | let orRes = x | y; |
| 24 | - AssertType(orRes, "int"); | 24 | + AssertType(orRes, "number"); |
| 25 | 25 | ||
| 26 | let xorRes = x ^ y; | 26 | let xorRes = x ^ y; |
| 27 | - AssertType(xorRes, "int"); | 27 | + AssertType(xorRes, "number"); |
| 28 | 28 | ||
| 29 | let shlRes = x << y; | 29 | let shlRes = x << y; |
| 30 | AssertType(shlRes, "int"); | 30 | AssertType(shlRes, "int"); |
| @@ -18,5 +18,5 @@ declare function AssertType(value:any, type:string):void; | |||
| 18 | let num1 : number = 1; | 18 | let num1 : number = 1; |
| 19 | let num2 : number = 2; | 19 | let num2 : number = 2; |
| 20 | let ans = num1 % num2; | 20 | let ans = num1 % num2; |
| 21 | - AssertType(ans, "int"); | 21 | + AssertType(ans, "number"); |
| 22 | } | 22 | } |