已合并
Range Analysis of MUL and MOD #4561
AtomGit-Bot创建于 2023年8月1日
Range Analysis of MUL and MOD #4561
已合并
从refs/pull/4561/head合入到master
共 9 个文件变更+159-15
| @@ -102,9 +102,10 @@ 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 const inline std::vector<int32_t> rangeBounds_ = { INT32_MIN, INT32_MIN + 1, | 105 | + static constexpr int32_t UINT18_MAX = (1 << 18) - 1; |
| 106 | + static const inline std::vector<int32_t> rangeBounds_ = { INT32_MIN, INT32_MIN + 1, -UINT18_MAX, -TYPED_ARRAY_ONHEAP_MAX, | ||
| 106 | -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1, TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, | 107 | -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1, TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, |
| 107 | - TYPED_ARRAY_ONHEAP_MAX * 3, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX }; | 108 | + TYPED_ARRAY_ONHEAP_MAX * 3, UINT18_MAX, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX }; |
| 108 | 109 | ||
| 109 | static RangeInfo NONE() | 110 | static RangeInfo NONE() |
| 110 | { | 111 | { |
| @@ -153,11 +154,81 @@ public: | |||
| 153 | 154 | ||
| 154 | RangeInfo operator+ (const RangeInfo &rhs) const | 155 | RangeInfo operator+ (const RangeInfo &rhs) const |
| 155 | { | 156 | { |
| 157 | + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 156 | int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_; | 158 | int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_; |
| 157 | int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_; | 159 | int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_; |
| 158 | return RangeInfo(nmin, nmax); | 160 | return RangeInfo(nmin, nmax); |
| 159 | } | 161 | } |
| 160 | 162 | ||
| 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_) }); | ||
尝试调用MaybeMulOverflow ![]() ![]() | |||
| 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 | + | ||
| 161 | bool MaybeSubOverflow(const RangeInfo &rhs) const | 232 | bool MaybeSubOverflow(const RangeInfo &rhs) const |
| 162 | { | 233 | { |
| 163 | return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_); | 234 | return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_); |
| @@ -175,6 +246,7 @@ public: | |||
| 175 | 246 | ||
| 176 | RangeInfo operator- (const RangeInfo &rhs) const | 247 | RangeInfo operator- (const RangeInfo &rhs) const |
| 177 | { | 248 | { |
| 249 | + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); | ||
| 178 | int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_; | 250 | int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_; |
| 179 | int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_; | 251 | int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_; |
| 180 | return RangeInfo(nmin, nmax); | 252 | return RangeInfo(nmin, nmax); |
| @@ -190,6 +262,8 @@ public: | |||
| 190 | 262 | ||
| 191 | RangeInfo SHR(const RangeInfo &rhs) const | 263 | RangeInfo SHR(const RangeInfo &rhs) const |
| 192 | { | 264 | { |
| 265 | + ASSERT(min_ <= max_); | ||
| 266 | + ASSERT(rhs.max_ == rhs.min_); | ||
| 193 | if (MaybeShrOverflow(rhs)) { | 267 | if (MaybeShrOverflow(rhs)) { |
| 194 | // assume no overflow occurs since overflow will lead to deopt | 268 | // assume no overflow occurs since overflow will lead to deopt |
| 195 | return RangeInfo(0, std::max(0, GetMax())); | 269 | return RangeInfo(0, std::max(0, GetMax())); |
| @@ -204,6 +278,7 @@ public: | |||
| 204 | 278 | ||
| 205 | RangeInfo ASHR(const RangeInfo &rhs) const | 279 | RangeInfo ASHR(const RangeInfo &rhs) const |
| 206 | { | 280 | { |
| 281 | + ASSERT(min_ <= max_); | ||
| 207 | ASSERT(rhs.max_ == rhs.min_); | 282 | ASSERT(rhs.max_ == rhs.min_); |
| 208 | int32_t shift = rhs.max_ & 0x1f; // 0x1f : shift bits | 283 | int32_t shift = rhs.max_ & 0x1f; // 0x1f : shift bits |
| 209 | int32_t nmin = min_ >> shift; | 284 | int32_t nmin = min_ >> shift; |
| @@ -355,7 +355,9 @@ 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 | - builder_.Int32CheckRightIsZero(right); | 358 | + if(GetRange(right).MaybeZero()) { |
| 359 | + builder_.Int32CheckRightIsZero(right); | ||
| 360 | + } | ||
| 359 | result = CalculateInts<Op>(left, right); | 361 | result = CalculateInts<Op>(left, right); |
| 360 | UpdateRange(result, GetRange(gate)); | 362 | UpdateRange(result, GetRange(gate)); |
| 361 | acc_.SetMachineType(gate, MachineType::I32); | 363 | acc_.SetMachineType(gate, MachineType::I32); |
| @@ -576,6 +578,9 @@ GateRef NumberSpeculativeLowering::CalculateInts(GateRef left, GateRef right) | |||
| 576 | break; | 578 | break; |
| 577 | } | 579 | } |
| 578 | case TypedBinOp::TYPED_MUL: | 580 | case TypedBinOp::TYPED_MUL: |
| 581 | + if(!leftRange.MaybeMulOverflowOrUnderflow(rightRange)) { | ||
| 582 | + return builder_.Int32Mul(left, right); | ||
| 583 | + } | ||
| 579 | res = builder_.MulWithOverflow(left, right); | 584 | res = builder_.MulWithOverflow(left, right); |
| 580 | break; | 585 | break; |
| 581 | case TypedBinOp::TYPED_MOD: { | 586 | case TypedBinOp::TYPED_MOD: { |
| @@ -139,6 +139,12 @@ 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; | ||
| 142 | case TypedBinOp::TYPED_SHR: | 148 | case TypedBinOp::TYPED_SHR: |
| 143 | range = GetRangeOfShift<TypedBinOp::TYPED_SHR>(gate); | 149 | range = GetRangeOfShift<TypedBinOp::TYPED_SHR>(gate); |
| 144 | break; | 150 | break; |
| @@ -181,7 +187,6 @@ GateRef RangeAnalysis::VisitRangeGuard(GateRef gate) | |||
| 181 | template<TypedBinOp Op> | 187 | template<TypedBinOp Op> |
| 182 | RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) | 188 | RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) |
| 183 | { | 189 | { |
| 184 | - ASSERT((Op == TypedBinOp::TYPED_ADD) || (Op == TypedBinOp::TYPED_SUB)); | ||
| 185 | auto left = GetRange(acc_.GetValueIn(gate, 0)); | 190 | auto left = GetRange(acc_.GetValueIn(gate, 0)); |
| 186 | auto right = GetRange(acc_.GetValueIn(gate, 1)); | 191 | auto right = GetRange(acc_.GetValueIn(gate, 1)); |
| 187 | if (left.IsNone() || right.IsNone()) { | 192 | if (left.IsNone() || right.IsNone()) { |
| @@ -192,6 +197,10 @@ RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) | |||
| 192 | return left + right; | 197 | return left + right; |
| 193 | case TypedBinOp::TYPED_SUB: | 198 | case TypedBinOp::TYPED_SUB: |
| 194 | return left - right; | 199 | return left - right; |
| 200 | + case TypedBinOp::TYPED_MOD: | ||
| 201 | + return left % right; | ||
| 202 | + case TypedBinOp::TYPED_MUL: | ||
| 203 | + return left * right; | ||
| 195 | default: | 204 | default: |
| 196 | return RangeInfo::ANY(); | 205 | return RangeInfo::ANY(); |
| 197 | } | 206 | } |
| @@ -321,6 +330,12 @@ void RangeAnalysis::PrintRangeInfo() const | |||
| 321 | case TypedBinOp::TYPED_ASHR: | 330 | case TypedBinOp::TYPED_ASHR: |
| 322 | log += " ashr"; | 331 | log += " ashr"; |
| 323 | break; | 332 | break; |
| 333 | + case TypedBinOp::TYPED_MOD: | ||
| 334 | + log += " mod"; | ||
| 335 | + break; | ||
| 336 | + case TypedBinOp::TYPED_MUL: | ||
| 337 | + log += " mul"; | ||
| 338 | + break; | ||
| 324 | default: | 339 | default: |
| 325 | log += " other"; | 340 | log += " other"; |
| 326 | break; | 341 | break; |
| @@ -14,7 +14,6 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | - | ||
| 18 | 17 | ||
| 19 | 18 | ||
| 20 | 19 | ||
| @@ -206,10 +205,6 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 206 | switch (bytecodeInfo.GetOpcode()) { | 205 | switch (bytecodeInfo.GetOpcode()) { |
| 207 | case EcmaOpcode::LDNAN: | 206 | case EcmaOpcode::LDNAN: |
| 208 | case EcmaOpcode::LDINFINITY: | 207 | 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: | ||
| 213 | case EcmaOpcode::TONUMBER_IMM8: | 208 | case EcmaOpcode::TONUMBER_IMM8: |
| 214 | case EcmaOpcode::NEG_IMM8: | 209 | case EcmaOpcode::NEG_IMM8: |
| 215 | case EcmaOpcode::EXP_IMM8_V8: | 210 | case EcmaOpcode::EXP_IMM8_V8: |
| @@ -219,6 +214,9 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 219 | case EcmaOpcode::ASHR2_IMM8_V8: | 214 | case EcmaOpcode::ASHR2_IMM8_V8: |
| 220 | case EcmaOpcode::SHR2_IMM8_V8: | 215 | case EcmaOpcode::SHR2_IMM8_V8: |
| 221 | case EcmaOpcode::NOT_IMM8: | 216 | case EcmaOpcode::NOT_IMM8: |
| 217 | + case EcmaOpcode::AND2_IMM8_V8: | ||
| 218 | + case EcmaOpcode::OR2_IMM8_V8: | ||
| 219 | + case EcmaOpcode::XOR2_IMM8_V8: | ||
| 222 | return SetIntType(gate); | 220 | return SetIntType(gate); |
| 223 | case EcmaOpcode::LDBIGINT_ID16: | 221 | case EcmaOpcode::LDBIGINT_ID16: |
| 224 | return SetBigIntType(gate); | 222 | return SetBigIntType(gate); |
| @@ -261,6 +259,8 @@ bool MethodTypeInfer::Infer(GateRef gate) | |||
| 261 | return InferSub2(gate); | 259 | return InferSub2(gate); |
| 262 | case EcmaOpcode::MUL2_IMM8_V8: | 260 | case EcmaOpcode::MUL2_IMM8_V8: |
| 263 | return InferMul2(gate); | 261 | 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,6 +553,31 @@ 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 | + | ||
| 556 | /* | 581 | /* |
| 557 | * Type Infer rule(satisfy commutative law): | 582 | * Type Infer rule(satisfy commutative law): |
| 558 | * in type lowering, both elements will be changed to float64 firstly. | 583 | * in type lowering, both elements will be changed to float64 firstly. |
| @@ -84,6 +84,7 @@ 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); | ||
| 87 | bool InferDiv2(GateRef gate); | 88 | bool InferDiv2(GateRef gate); |
| 88 | bool InferIncDec(GateRef gate); | 89 | bool InferIncDec(GateRef gate); |
| 89 | bool InferToNumberic(GateRef gate); | 90 | bool InferToNumberic(GateRef gate); |
| @@ -34,7 +34,7 @@ void TypeMCRLowering::RunTypeMCRLowering() | |||
| 34 | if (IsLogEnabled()) { | 34 | if (IsLogEnabled()) { |
| 35 | LOG_COMPILER(INFO) << ""; | 35 | LOG_COMPILER(INFO) << ""; |
| 36 | LOG_COMPILER(INFO) << "\033[34m" << "==================" | 36 | LOG_COMPILER(INFO) << "\033[34m" << "==================" |
| 37 | - << " after TypeMCRlowering " | 37 | + << " After TypeMCRlowering " |
| 38 | << "[" << GetMethodName() << "] " | 38 | << "[" << GetMethodName() << "] " |
| 39 | << "==================" << "\033[0m"; | 39 | << "==================" << "\033[0m"; |
| 40 | circuit_->PrintAllGatesWithBytecode(); | 40 | circuit_->PrintAllGatesWithBytecode(); |
| @@ -68,6 +68,26 @@ 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 | + | ||
| 71 | testcase1(); // 8 | 91 | testcase1(); // 8 |
| 72 | testcase2(); // 8 | 92 | testcase2(); // 8 |
| 73 | 93 | ||
| @@ -75,4 +95,7 @@ testcase3(); | |||
| 75 | testcase4(); | 95 | testcase4(); |
| 76 | 96 | ||
| 77 | testcase5(); | 97 | testcase5(); |
| 78 | -testcase6(); | 98 | +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, "number"); | 21 | + AssertType(andRes, "int"); |
| 22 | 22 | ||
| 23 | let orRes = x | y; | 23 | let orRes = x | y; |
| 24 | - AssertType(orRes, "number"); | 24 | + AssertType(orRes, "int"); |
| 25 | 25 | ||
| 26 | let xorRes = x ^ y; | 26 | let xorRes = x ^ y; |
| 27 | - AssertType(xorRes, "number"); | 27 | + AssertType(xorRes, "int"); |
| 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, "number"); | 21 | + AssertType(ans, "int"); |
| 22 | } | 22 | } |


TYPED_ARRAY_ONHEAP_MAX << 6换个名字