Support inlining of min/max

* Add option to specify expect_output in .ts comments
* Add tests

Change-Id: Iea5f99c053854b17a53cc8d98e192868b39d0738
Signed-off-by: Andrey Efremov <efremov.andrey@huawei-partners.com>
This commit is contained in:
Andrey Efremov 2024-03-07 20:38:27 +08:00 committed by Chernykh Sergey
parent d0896b61f3
commit 158448ab63
25 changed files with 1154 additions and 171 deletions

View File

@ -57,8 +57,8 @@
V("log10", Log10, 1, MathLog10) /* Math.log10 ( x ) */ \
V("log1p", Log1p, 1, MathLog1p) /* Math.log1p ( x ) */ \
V("log2", Log2, 1, MathLog2) /* Math.log2 ( x ) */ \
V("max", Max, 2, INVALID) /* Math.max ( ...args ) */ \
V("min", Min, 2, INVALID) /* Math.min ( ...args ) */ \
V("max", Max, 2, MathMax) /* Math.max ( ...args ) */ \
V("min", Min, 2, MathMin) /* Math.min ( ...args ) */ \
V("pow", Pow, 2, MathPow) /* Math.pow ( base, exponent ) */ \
V("random", Random, 0, INVALID) /* Math.random ( ) */ \
V("round", Round, 1, INVALID) /* Math.round ( x ) */ \

View File

@ -164,8 +164,10 @@ namespace panda::ecmascript::kungfu {
V(MathExpm1) \
V(MathPow) \
V(MathAbs) \
V(MathMin) \
V(MathMax) \
V(TYPED_BUILTINS_INLINE_FIRST = MathAcos) \
V(TYPED_BUILTINS_INLINE_LAST = MathAbs)
V(TYPED_BUILTINS_INLINE_LAST = MathMax)
class BuiltinsStubCSigns {
public:
@ -327,6 +329,10 @@ public:
return ConstantIndex::MATH_FLOOR_FUNCTION_INDEX;
case BuiltinsStubCSigns::ID::SQRT:
return ConstantIndex::MATH_SQRT_FUNCTION_INDEX;
case BuiltinsStubCSigns::ID::MathMin:
return ConstantIndex::MATH_MIN_INDEX;
case BuiltinsStubCSigns::ID::MathMax:
return ConstantIndex::MATH_MAX_INDEX;
case BuiltinsStubCSigns::ID::LocaleCompare:
return ConstantIndex::LOCALE_COMPARE_FUNCTION_INDEX;
case BuiltinsStubCSigns::ID::SORT:

View File

@ -93,7 +93,11 @@ class PostSchedule;
V(Int32LSR, Lsr, MachineType::I32) \
V(Int64LSR, Lsr, MachineType::I64) \
V(Int32ASR, Asr, MachineType::I32) \
V(Int64ASR, Asr, MachineType::I64)
V(Int64ASR, Asr, MachineType::I64) \
V(Int32Min, Min, MachineType::I32) \
V(DoubleMin, Min, MachineType::F64) \
V(Int32Max, Max, MachineType::I32) \
V(DoubleMax, Max, MachineType::F64)
#define UNARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(V) \
V(BoolNot, Rev, MachineType::I1) \
@ -136,7 +140,9 @@ class PostSchedule;
V(SExtInt16ToInt64, Sext, MachineType::I64) \
V(SExtInt16ToInt32, Sext, MachineType::I32) \
V(SExtInt8ToInt32, Sext, MachineType::I32) \
V(SExtInt8ToInt64, Sext, MachineType::I64)
V(SExtInt8ToInt64, Sext, MachineType::I64) \
V(Abs, Abs, MachineType::I32) \
V(FAbs, Abs, MachineType::F64)
#define UNARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH_PRIVATE(V) \
V(ChangeTaggedPointerToInt64, TaggedToInt64, MachineType::I64)

View File

@ -207,6 +207,9 @@ void LLVMIRBuilder::InitializeHandlers()
{OpCode::EXTRACT_VALUE, &LLVMIRBuilder::HandleExtractValue},
{OpCode::SQRT, &LLVMIRBuilder::HandleSqrt},
{OpCode::EXP, &LLVMIRBuilder::HandleExp},
{OpCode::ABS, &LLVMIRBuilder::HandleAbs},
{OpCode::MIN, &LLVMIRBuilder::HandleMin},
{OpCode::MAX, &LLVMIRBuilder::HandleMax},
{OpCode::READSP, &LLVMIRBuilder::HandleReadSp},
{OpCode::FINISH_ALLOCATE, &LLVMIRBuilder::HandleFinishAllocate},
};
@ -2023,6 +2026,108 @@ void LLVMIRBuilder::VisitExp([[maybe_unused]] GateRef gate, [[maybe_unused]] Gat
#endif
}
template<typename... Ts>
static llvm::CallInst *BuildLLVMIntrinsic(llvm::IRBuilder<> *builder, llvm::Intrinsic::ID llvmId, Ts... inputs)
{
static_assert((std::is_same_v<Ts, llvm::Value *> && ...));
if constexpr (sizeof...(inputs) == 1) {
return builder->CreateUnaryIntrinsic(llvmId, inputs...);
} else {
static_assert(sizeof...(inputs) == 2);
return builder->CreateBinaryIntrinsic(llvmId, inputs...);
}
}
void LLVMIRBuilder::HandleAbs(GateRef gate)
{
VisitAbs(gate, acc_.GetIn(gate, 0));
}
void LLVMIRBuilder::VisitAbs(GateRef gate, GateRef e1)
{
auto machineType = acc_.GetMachineType(gate);
ASSERT(acc_.GetMachineType(e1) == machineType);
llvm::Intrinsic::ID llvmId = 0;
auto *builder = llvm::unwrap(builder_);
llvm::Value *value = llvm::unwrap(GetLValue(e1));
LLVMValueRef result;
if (machineType == MachineType::I32) {
llvmId = llvm::Intrinsic::abs;
llvm::Type *type = llvm::Type::getInt1Ty(*llvm::unwrap(context_));
llvm::Value *poison = llvm::Constant::getIntegerValue(type, llvm::APInt(1, 0));
result = llvm::wrap(BuildLLVMIntrinsic(builder, llvmId, value, poison));
} else if (machineType == MachineType::F64) {
llvmId = llvm::Intrinsic::fabs;
result = llvm::wrap(BuildLLVMIntrinsic(builder, llvmId, value));
} else {
LOG_ECMA(FATAL) << "`Abs` type should be untagged double or signed int";
UNREACHABLE();
}
Bind(gate, result);
if (IsLogEnabled()) {
SetDebugInfo(gate, result);
}
}
void LLVMIRBuilder::HandleMin(GateRef gate)
{
VisitMin(gate, acc_.GetIn(gate, 0), acc_.GetIn(gate, 1U));
}
void LLVMIRBuilder::VisitMin(GateRef gate, GateRef e1, GateRef e2)
{
auto machineType = acc_.GetMachineType(gate);
ASSERT(acc_.GetMachineType(e1) == machineType);
ASSERT(acc_.GetMachineType(e2) == machineType);
llvm::Intrinsic::ID llvmId = 0;
if (machineType == MachineType::I32) {
llvmId = llvm::Intrinsic::smin;
} else if (machineType == MachineType::F64) {
llvmId = llvm::Intrinsic::minimum;
} else {
LOG_ECMA(FATAL) << "`Min` type should be untagged double or signed int";
UNREACHABLE();
}
VisitIntrinsic(gate, llvmId, e1, e2);
}
void LLVMIRBuilder::HandleMax(GateRef gate)
{
VisitMax(gate, acc_.GetIn(gate, 0), acc_.GetIn(gate, 1U));
}
void LLVMIRBuilder::VisitMax(GateRef gate, GateRef e1, GateRef e2)
{
auto machineType = acc_.GetMachineType(gate);
ASSERT(acc_.GetMachineType(e1) == machineType);
ASSERT(acc_.GetMachineType(e2) == machineType);
llvm::Intrinsic::ID llvmId = 0;
if (machineType == MachineType::I32) {
llvmId = llvm::Intrinsic::smax;
} else if (machineType == MachineType::F64) {
llvmId = llvm::Intrinsic::maximum;
} else {
LOG_ECMA(FATAL) << "`Max` type should be untagged double or signed int";
UNREACHABLE();
}
VisitIntrinsic(gate, llvmId, e1, e2);
}
template<typename... Ts>
void LLVMIRBuilder::VisitIntrinsic(GateRef gate, llvm::Intrinsic::ID llvmId, Ts... inputs)
{
static_assert((std::is_same_v<Ts, GateRef> && ...));
auto *builder = llvm::unwrap(builder_);
LLVMValueRef result = llvm::wrap(BuildLLVMIntrinsic(builder, llvmId, llvm::unwrap(GetLValue(inputs))...));
Bind(gate, result);
if (IsLogEnabled()) {
SetDebugInfo(gate, result);
}
}
LLVMIntPredicate LLVMIRBuilder::ConvertLLVMPredicateFromICMP(ICmpCondition cond)
{
switch (cond) {

View File

@ -311,6 +311,8 @@ private:
LLVMValueRef CanonicalizeToPtr(LLVMValueRef value, LLVMTypeRef ptrType) const;
LLVMValueRef GetCurrentFrameType(LLVMValueRef currentSpFrameAddr);
void SetFunctionCallConv();
template<typename... Ts>
void VisitIntrinsic(GateRef gate, unsigned llvmId, Ts... inputs);
bool IsLogEnabled() const
{

View File

@ -103,6 +103,9 @@ enum class CallExceptionKind : bool {
V(ExtractValue, (GateRef gate, GateRef e1, GateRef e2)) \
V(Sqrt, (GateRef gate, GateRef e1)) \
V(Exp, (GateRef gate, GateRef e1, GateRef e2)) \
V(Abs, (GateRef gate, GateRef e1)) \
V(Min, (GateRef gate, GateRef e1, GateRef e2)) \
V(Max, (GateRef gate, GateRef e1, GateRef e2)) \
V(ReadSp, (GateRef gate)) \
V(FinishAllocate, (GateRef gate, GateRef e1))

View File

@ -36,11 +36,13 @@ namespace panda::ecmascript::kungfu {
V(Lsr, LSR, GateFlags::NONE_FLAG, 0, 0, 2) \
V(Asr, ASR, GateFlags::NONE_FLAG, 0, 0, 2) \
V(Sqrt, SQRT, GateFlags::NO_WRITE, 0, 0, 1) \
V(Min, MIN, GateFlags::NO_WRITE, 0, 0, 2) \
V(Max, MAX, GateFlags::NO_WRITE, 0, 0, 2) \
V(AddWithOverflow, ADD_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \
V(SubWithOverflow, SUB_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \
V(MulWithOverflow, MUL_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \
V(ExtractValue, EXTRACT_VALUE, GateFlags::NONE_FLAG, 0, 0, 2)
#define LCR_UNARY_GATE_META_DATA_CACHE_LIST(V) \
V(Zext, ZEXT, GateFlags::NONE_FLAG, 0, 0, 1) \
V(Sext, SEXT, GateFlags::NONE_FLAG, 0, 0, 1) \
@ -56,7 +58,8 @@ namespace panda::ecmascript::kungfu {
V(UnsignedFloatToInt, UNSIGNED_FLOAT_TO_INT, GateFlags::NONE_FLAG, 0, 0, 1) \
V(TruncFloatToInt64, TRUNC_FLOAT_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \
V(TruncFloatToInt32, TRUNC_FLOAT_TO_INT32, GateFlags::NONE_FLAG, 0, 0, 1) \
V(Bitcast, BITCAST, GateFlags::NONE_FLAG, 0, 0, 1)
V(Bitcast, BITCAST, GateFlags::NONE_FLAG, 0, 0, 1) \
V(Abs, ABS, GateFlags::NO_WRITE, 0, 0, 1)
#define LCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \
V(ReadSp, READSP, GateFlags::NONE_FLAG, 0, 0, 0) \

View File

@ -81,8 +81,16 @@ namespace panda::ecmascript::kungfu {
V(MathExp, MATH_EXP, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathExpm1, MATH_EXPM1, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathAbs, MATH_ABS, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathAbsInt32, MATH_ABS_INT32, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathAbsDouble, MATH_ABS_DOUBLE, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathPow, MATH_POW, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathCbrt, MATH_CBRT, GateFlags::NO_WRITE, 1, 1, 1) \
V(MathMin, MATH_MIN, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathMinInt32, MATH_MIN_INT32, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathMinDouble, MATH_MIN_DOUBLE, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathMax, MATH_MAX, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathMaxInt32, MATH_MAX_INT32, GateFlags::NO_WRITE, 1, 1, 2) \
V(MathMaxDouble, MATH_MAX_DOUBLE, GateFlags::NO_WRITE, 1, 1, 2) \
MCR_BINARY_GATE_META_DATA_CACHE_LIST(V)
#define MCR_GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \

View File

@ -128,6 +128,12 @@ void NativeInlineLowering::RunNativeInlineLowering()
case BuiltinsStubCSigns::ID::MathCbrt:
TryInlineMathUnaryBuiltin(gate, argc, id, circuit_->MathCbrt());
break;
case BuiltinsStubCSigns::ID::MathMin:
TryInlineMathMinMaxBuiltin(gate, argc, id, circuit_->MathMin(), base::POSITIVE_INFINITY);
break;
case BuiltinsStubCSigns::ID::MathMax:
TryInlineMathMinMaxBuiltin(gate, argc, id, circuit_->MathMax(), -base::POSITIVE_INFINITY);
break;
default:
break;
}
@ -195,4 +201,34 @@ void NativeInlineLowering::TryInlineMathBinaryBuiltin(GateRef gate, size_t argc,
return;
}
void NativeInlineLowering::TryInlineMathMinMaxBuiltin(GateRef gate, size_t argc, BuiltinsStubCSigns::ID id,
const GateMetaData* op, double defaultValue)
{
Environment env(gate, circuit_, &builder_);
if (!Uncheck()) {
builder_.CallTargetCheck(gate, acc_.GetValueIn(gate, argc + 1),
builder_.IntPtr(static_cast<int64_t>(id)));
}
if (argc == 0) {
GateRef ret = builder_.DoubleToTaggedDoublePtr(builder_.Double(defaultValue));
acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), ret);
return;
}
GateRef ret = acc_.GetValueIn(gate, 1);
if (argc == 1) {
auto param_check = builder_.TaggedIsNumber(ret);
builder_.DeoptCheck(param_check, acc_.GetFrameState(gate), DeoptType::BUILTIN_INLINING_TYPE_GUARD);
if (acc_.GetGateType(ret).IsAnyType()) {
acc_.SetGateType(ret, GateType::NumberType());
}
acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), ret);
return;
}
for (size_t i = 2; i <= argc; i++) {
auto param = acc_.GetValueIn(gate, i);
ret = builder_.BuildMathBuiltinOp(op, {ret, param});
}
acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), ret);
}
} // namespace panda::ecmascript

View File

@ -42,9 +42,10 @@ public:
private:
std::optional<size_t> GetArgc(GateRef gate);
void TryInlineStringFromCharCode(GateRef gate, size_t argc);
void TryInlineMathMinWithOneArg(GateRef gate, size_t argc);
void TryInlineMathUnaryBuiltin(GateRef gate, size_t argc, BuiltinsStubCSigns::ID id, const GateMetaData* op);
void TryInlineMathBinaryBuiltin(GateRef gate, size_t argc, BuiltinsStubCSigns::ID id, const GateMetaData* op);
void TryInlineMathMinMaxBuiltin(GateRef gate, size_t argc, BuiltinsStubCSigns::ID id, const GateMetaData* op,
double defaultValue);
bool EnableLog() const
{

View File

@ -72,6 +72,49 @@ GateRef NumberSpeculativeRetype::SetOutputType(GateRef gate, TypeInfo type)
SetOutputTypeInfo(gate, type);
return old == type ? Circuit::NullGate() : gate;
}
TypeInfo NumberSpeculativeRetype::GetNumberTypeInfo(GateRef gate)
{
TypeInfo typeInfo = GetOutputTypeInfo(gate);
switch (typeInfo) {
case TypeInfo::INT1:
case TypeInfo::INT32:
case TypeInfo::HOLE_INT:
return TypeInfo::INT32;
case TypeInfo::UINT32:
case TypeInfo::FLOAT64:
case TypeInfo::HOLE_DOUBLE:
return TypeInfo::FLOAT64;
case TypeInfo::NONE:
case TypeInfo::TAGGED: {
GateType gateType = acc_.GetGateType(gate);
if (gateType.IsIntType() || gateType.IsBooleanType()) {
return TypeInfo::INT32;
} else if (gateType.IsDoubleType()) {
return TypeInfo::FLOAT64;
} else {
return TypeInfo::TAGGED;
}
}
case TypeInfo::CHAR:
return TypeInfo::TAGGED;
}
UNREACHABLE();
}
static TypeInfo GetCommonTypeInfo(TypeInfo left, TypeInfo right)
{
if (left == TypeInfo::TAGGED || right == TypeInfo::TAGGED) {
return TypeInfo::TAGGED;
}
ASSERT(left == TypeInfo::INT32 || left == TypeInfo::FLOAT64);
ASSERT(right == TypeInfo::INT32 || right == TypeInfo::FLOAT64);
if (left == right) {
return left;
}
return TypeInfo::FLOAT64;
}
void NumberSpeculativeRetype::setState(NumberSpeculativeRetype::State state)
{
state_ = state;
@ -155,10 +198,13 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate)
case OpCode::MATH_TANH:
case OpCode::MATH_POW:
case OpCode::MATH_CBRT:
return VisitMathBuiltin(gate);
return VisitMathDoubleParamsBuiltin(gate);
case OpCode::MATH_ABS:
return VisitMathAbs(gate);
case OpCode::MATH_MIN:
case OpCode::MATH_MAX:
return VisitMathTaggedNumberParamsBuiltin(gate);
case OpCode::JS_BYTECODE:
case OpCode::RUNTIME_CALL:
case OpCode::PRIMITIVE_TYPE_CHECK:
case OpCode::STABLE_ARRAY_CHECK:
case OpCode::TYPED_ARRAY_CHECK:
@ -1037,8 +1083,12 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToTagged(GateRef gate, GateType
{
TypeInfo output = GetOutputTypeInfo(gate);
switch (output) {
case TypeInfo::INT1:
case TypeInfo::INT1: {
if (gateType.IsNumberType() && convert != ConvertToNumber::DISABLE) {
return builder_.ConvertInt32ToTaggedInt(builder_.BooleanToInt32(gate));
}
return builder_.ConvertBoolToTaggedBoolean(gate);
}
case TypeInfo::INT32:
return builder_.ConvertInt32ToTaggedInt(gate);
case TypeInfo::UINT32:
@ -1325,7 +1375,7 @@ GateRef NumberSpeculativeRetype::VisitTypeConvert(GateRef gate)
return Circuit::NullGate();
}
GateRef NumberSpeculativeRetype::VisitMathBuiltin(GateRef gate)
GateRef NumberSpeculativeRetype::VisitMathDoubleParamsBuiltin(GateRef gate)
{
if (IsRetype()) {
return SetOutputType(gate, GateType::DoubleType());
@ -1335,23 +1385,77 @@ GateRef NumberSpeculativeRetype::VisitMathBuiltin(GateRef gate)
size_t valueNum = acc_.GetNumValueIn(gate);
for (size_t i = 0; i < valueNum; ++i) {
GateRef input = acc_.GetValueIn(gate, i);
acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(input, GateType::NumberType(), ConvertToNumber::BOOL_ONLY), i);
acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(input, GateType::NumberType(),
ConvertToNumber::BOOL_ONLY), i);
}
acc_.ReplaceStateIn(gate, builder_.GetState());
acc_.ReplaceDependIn(gate, builder_.GetDepend());
return Circuit::NullGate();
}
GateRef NumberSpeculativeRetype::VisitMathAbs(GateRef gate)
const GateMetaData *NumberSpeculativeRetype::GetNewMeta(OpCode op, TypeInfo type)
{
if (type == TypeInfo::INT32) {
switch (op) {
case OpCode::MATH_ABS:
return circuit_->MathAbsInt32();
case OpCode::MATH_MIN:
return circuit_->MathMinInt32();
case OpCode::MATH_MAX:
return circuit_->MathMaxInt32();
default:
UNREACHABLE();
}
} else if (type == TypeInfo::FLOAT64) {
switch (op) {
case OpCode::MATH_ABS:
return circuit_->MathAbsDouble();
case OpCode::MATH_MIN:
return circuit_->MathMinDouble();
case OpCode::MATH_MAX:
return circuit_->MathMaxDouble();
default:
UNREACHABLE();
}
} else {
UNREACHABLE();
return nullptr;
}
}
GateRef NumberSpeculativeRetype::VisitMathTaggedNumberParamsBuiltin(GateRef gate)
{
size_t valueNum = acc_.GetNumValueIn(gate);
ASSERT(valueNum <= 2U);
if (IsRetype()) {
return SetOutputType(gate, GateType::NumberType());
TypeInfo type = GetNumberTypeInfo(acc_.GetValueIn(gate, 0));
if (valueNum > 1U) {
TypeInfo secondInputType = GetNumberTypeInfo(acc_.GetValueIn(gate, 1U));
type = GetCommonTypeInfo(type, secondInputType);
}
return SetOutputType(gate, type);
}
ASSERT(IsConvert());
TypeInfo type = GetOutputTypeInfo(gate); // load type computed in retype phase
if (type != TypeInfo::TAGGED) {
const GateMetaData* meta = GetNewMeta(acc_.GetOpCode(gate), type);
acc_.SetMetaData(gate, meta);
}
Environment env(gate, circuit_, &builder_);
ASSERT(acc_.GetNumValueIn(gate) == 1);
GateRef input = acc_.GetValueIn(gate, 0);
acc_.ReplaceValueIn(gate, CheckAndConvertToTagged(input, GateType::NumberType(), ConvertToNumber::BOOL_ONLY), 0);
for (size_t i = 0; i < valueNum; ++i) {
GateRef input = acc_.GetValueIn(gate, i);
if (type == TypeInfo::INT32) {
input = CheckAndConvertToInt32(input, GateType::IntType());
} else if (type == TypeInfo::FLOAT64) {
input = CheckAndConvertToFloat64(input, acc_.GetGateType(input),
ConvertToNumber::BOOL_ONLY);
} else {
ASSERT(type == TypeInfo::TAGGED);
input = CheckAndConvertToTagged(input, GateType::NumberType(),
ConvertToNumber::BOOL_ONLY);
}
acc_.ReplaceValueIn(gate, input, i);
}
acc_.ReplaceStateIn(gate, builder_.GetState());
acc_.ReplaceDependIn(gate, builder_.GetDepend());
return Circuit::NullGate();

View File

@ -70,6 +70,7 @@ private:
GateRef SetOutputType(GateRef gate, GateType type);
GateRef SetOutputType(GateRef gate, Representation rep);
GateRef SetOutputType(GateRef gate, TypeInfo type);
TypeInfo GetNumberTypeInfo(GateRef gate);
GateRef VisitPhi(GateRef gate);
GateRef VisitConstant(GateRef gate);
GateRef VisitTypedBinaryOp(GateRef gate);
@ -89,8 +90,9 @@ private:
GateRef VisitNumberCompare(GateRef gate);
GateRef VisitNumberShiftAndLogical(GateRef gate);
GateRef VisitNumberToString(GateRef gate);
GateRef VisitMathBuiltin(GateRef gate);
GateRef VisitMathAbs(GateRef gate);
GateRef VisitMathDoubleParamsBuiltin(GateRef gate);
const GateMetaData *GetNewMeta(OpCode op, TypeInfo type);
GateRef VisitMathTaggedNumberParamsBuiltin(GateRef gate);
GateRef VisitBooleanJump(GateRef gate);
GateRef VisitRangeCheckPredicate(GateRef gate);
GateRef VisitIndexCheck(GateRef gate);

View File

@ -445,6 +445,7 @@ public:
CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
TypedNativeInlineLowering lowering(data->GetCircuit(),
&visitor,
data->GetPassContext(),
data->GetCompilerConfig(),
&chunk);
visitor.AddPass(&lowering);

View File

@ -81,12 +81,36 @@ GateRef TypedNativeInlineLowering::VisitGate(GateRef gate)
case OpCode::MATH_ABS:
LowerAbs(gate);
break;
case OpCode::MATH_ABS_INT32:
LowerIntAbs(gate);
break;
case OpCode::MATH_ABS_DOUBLE:
LowerDoubleAbs(gate);
break;
case OpCode::MATH_POW:
LowerMathPow(gate);
break;
case OpCode::MATH_CBRT:
LowerGeneralUnaryMath(gate, RTSTUB_ID(FloatCbrt));
break;
case OpCode::MATH_MIN:
LowerMinMax<false>(gate);
break;
case OpCode::MATH_MAX:
LowerMinMax<true>(gate);
break;
case OpCode::MATH_MIN_INT32:
LowerIntMinMax<false>(gate);
break;
case OpCode::MATH_MAX_INT32:
LowerIntMinMax<true>(gate);
break;
case OpCode::MATH_MIN_DOUBLE:
LowerDoubleMinMax<false>(gate);
break;
case OpCode::MATH_MAX_DOUBLE:
LowerDoubleMinMax<true>(gate);
break;
default:
break;
}
@ -239,17 +263,38 @@ void TypedNativeInlineLowering::LowerMathAtan2(GateRef gate)
// Int abs : The internal representation of an integer is inverse code,
// The absolute value of a negative number can be found by inverting it by adding one.
GateRef TypedNativeInlineLowering::BuildIntAbs(GateRef value)
{
ASSERT(acc_.GetMachineType(value) == MachineType::I32);
if (isLiteCG_) {
auto temp = builder_.Int32ASR(value, builder_.Int32(JSTaggedValue::INT_SIGN_BIT_OFFSET));
auto res = builder_.Int32Xor(value, temp);
return builder_.Int32Sub(res, temp);
}
return builder_.Abs(value);
}
// Float abs : A floating-point number is composed of mantissa and exponent.
// The length of mantissa will affect the precision of the number, and its sign will determine the sign of the number.
// The absolute value of a floating-point number can be found by setting mantissa sign bit to 0.
void TypedNativeInlineLowering::LowerAbs(GateRef gate)
GateRef TypedNativeInlineLowering::BuildDoubleAbs(GateRef value)
{
Environment env(gate, circuit_, &builder_);
Label exit(&builder_);
GateRef param = acc_.GetValueIn(gate, 0);
ASSERT(acc_.GetMachineType(value) == MachineType::F64);
if (isLiteCG_) {
// set the sign bit to 0 by shift left then right.
auto temp = builder_.Int64LSL(builder_.CastDoubleToInt64(value), builder_.Int64(1));
auto res = builder_.Int64LSR(temp, builder_.Int64(1));
return builder_.CastInt64ToFloat64(res);
}
return builder_.FAbs(value);
}
GateRef TypedNativeInlineLowering::BuildTNumberAbs(GateRef param)
{
ASSERT(!acc_.GetGateType(param).IsNJSValueType());
DEFVALUE(result, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant());
Label exit(&builder_);
Label isInt(&builder_);
Label notInt(&builder_);
Label isIntMin(&builder_);
@ -262,9 +307,7 @@ void TypedNativeInlineLowering::LowerAbs(GateRef gate)
BRANCH_CIR(builder_.Equal(value, builder_.Int32(INT32_MIN)), &isIntMin, &isResultInt);
builder_.Bind(&isResultInt);
{
auto temp = builder_.Int32ASR(value, builder_.Int32(JSTaggedValue::INT_SIGN_BIT_OFFSET));
auto res = builder_.Int32Xor(value, temp);
result = builder_.Int32ToTaggedPtr(builder_.Int32Sub(res, temp));
result = builder_.Int32ToTaggedPtr(BuildIntAbs(value));
builder_.Jump(&intExit);
}
builder_.Bind(&isIntMin);
@ -279,15 +322,262 @@ void TypedNativeInlineLowering::LowerAbs(GateRef gate)
builder_.Bind(&notInt);
{
auto value = builder_.GetDoubleOfTDouble(param);
// set the sign bit to 0 by shift left then right.
auto temp = builder_.Int64LSL(builder_.CastDoubleToInt64(value), builder_.Int64(1));
auto res = builder_.Int64LSR(temp, builder_.Int64(1));
result = builder_.DoubleToTaggedDoublePtr(builder_.CastInt64ToFloat64(res));
result = builder_.DoubleToTaggedDoublePtr(BuildDoubleAbs(value));
builder_.Jump(&exit);
}
builder_.Bind(&exit);
acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result);
return *result;
}
void TypedNativeInlineLowering::LowerAbs(GateRef gate)
{
GateRef value = acc_.GetValueIn(gate, 0);
Environment env(gate, circuit_, &builder_);
GateRef res = BuildTNumberAbs(value);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), res);
}
void TypedNativeInlineLowering::LowerIntAbs(GateRef gate)
{
Environment env(gate, circuit_, &builder_);
GateRef value = acc_.GetValueIn(gate, 0);
auto frameState = FindFrameState(gate);
builder_.DeoptCheck(builder_.NotEqual(value, builder_.Int32(INT32_MIN)), frameState, DeoptType::NOTINT3);
GateRef res = BuildIntAbs(value);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), res);
}
void TypedNativeInlineLowering::LowerDoubleAbs(GateRef gate)
{
GateRef value = acc_.GetValueIn(gate, 0);
Environment env(gate, circuit_, &builder_);
GateRef res = BuildDoubleAbs(value);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), res);
}
// for min select in1 if int1 < int2, in2 otherwise
template<bool IS_MAX>
GateRef TypedNativeInlineLowering::BuildIntMinMax(GateRef int1, GateRef int2, GateRef in1, GateRef in2)
{
Label entry(&builder_);
builder_.SubCfgEntry(&entry);
// int or tagged
VariableType type {acc_.GetMachineType(in1), acc_.GetGateType(in1)};
DEFVALUE(result, (&builder_), type, (IS_MAX ? in1 : in2));
Label left(&builder_);
Label exit(&builder_);
builder_.Branch(builder_.Int32LessThan(int1, int2), &left, &exit);
builder_.Bind(&left);
{
result = IS_MAX ? in2 : in1;
builder_.Jump(&exit);
}
builder_.Bind(&exit);
GateRef res = *result;
builder_.SubCfgExit();
return res;
}
template<bool IS_MAX>
GateRef TypedNativeInlineLowering::BuildIntMinMax(GateRef in1, GateRef in2)
{
ASSERT(acc_.GetMachineType(in1) == MachineType::I32);
ASSERT(acc_.GetMachineType(in2) == MachineType::I32);
if (isLiteCG_) {
return BuildIntMinMax<IS_MAX>(in1, in2, in1, in2);
}
return IS_MAX ? builder_.Int32Max(in1, in2) : builder_.Int32Min(in1, in2);
}
/* for min select:
* NaN if double1 or double2 is NaN
* in1 if double1 and double2 are equal and in1 is negative zero
* in1 if double1 < double2, in2 otherwise */
template<bool IS_MAX>
GateRef TypedNativeInlineLowering::BuildDoubleMinMax(GateRef double1, GateRef double2, GateRef in1, GateRef in2)
{
Label entry(&builder_);
builder_.SubCfgEntry(&entry);
GateRef nanValue = builder_.NanValue();
if (in1 != double1) { // case when in1 and in2 are tagged
nanValue = builder_.DoubleToTaggedDoublePtr(nanValue);
}
// double or tagged
VariableType type {acc_.GetMachineType(in1), acc_.GetGateType(in1)};
DEFVALUE(result, (&builder_), type, nanValue);
Label left(&builder_);
Label rightOrZeroOrNan(&builder_);
Label right(&builder_);
Label exit(&builder_);
Label equal(&builder_);
Label equalOrNan(&builder_);
builder_.Branch(builder_.DoubleLessThan(double1, double2), &left, &rightOrZeroOrNan);
builder_.Bind(&rightOrZeroOrNan);
{
builder_.Branch(builder_.DoubleGreaterThan(double1, double2), &right, &equalOrNan);
builder_.Bind(&equalOrNan);
{
builder_.Branch(builder_.DoubleEqual(double1, double2), &equal, &exit);
builder_.Bind(&equal);
{
// Whether to return in1 or in2 matters only in case of 0, -0
const double negZero = -0.0;
GateRef negZeroValue = builder_.CastDoubleToInt64(builder_.Double(negZero));
builder_.Branch(builder_.Equal(builder_.CastDoubleToInt64(double1), negZeroValue), &left, &right);
}
}
builder_.Bind(&right);
{
result = IS_MAX ? in1 : in2;
builder_.Jump(&exit);
}
}
builder_.Bind(&left);
{
result = IS_MAX ? in2 : in1;
builder_.Jump(&exit);
}
builder_.Bind(&exit);
GateRef res = *result;
builder_.SubCfgExit();
return res;
}
template<bool IS_MAX>
GateRef TypedNativeInlineLowering::BuildDoubleMinMax(GateRef in1, GateRef in2)
{
ASSERT(acc_.GetMachineType(in1) == MachineType::F64);
ASSERT(acc_.GetMachineType(in2) == MachineType::F64);
if (!isLiteCG_ && builder_.GetCompilationConfig()->IsAArch64()) {
return IS_MAX ? builder_.DoubleMax(in1, in2) : builder_.DoubleMin(in1, in2);
}
return BuildDoubleMinMax<IS_MAX>(in1, in2, in1, in2);
}
template<bool IS_MAX>
void TypedNativeInlineLowering::LowerTNumberMinMax(GateRef gate)
{
Environment env(gate, circuit_, &builder_);
GateRef in1 = acc_.GetValueIn(gate, 0);
GateRef in2 = acc_.GetValueIn(gate, 1);
GateRef nanValue = builder_.DoubleToTaggedDoublePtr(builder_.NanValue());
DEFVALUE(result, (&builder_), VariableType::JS_ANY(), nanValue);
DEFVALUE(double1, (&builder_), VariableType::FLOAT64(), builder_.Double(0));
DEFVALUE(double2, (&builder_), VariableType::FLOAT64(), builder_.Double(0));
Label isInt1(&builder_);
Label isInt2(&builder_);
Label isDouble1(&builder_);
Label isDouble2(&builder_);
Label doubleExit(&builder_);
Label exit(&builder_);
builder_.Branch(builder_.TaggedIsInt(in1), &isInt1, &isDouble1);
{
builder_.Bind(&isInt1);
GateRef int1 = builder_.GetInt32OfTInt(in1);
builder_.Branch(builder_.TaggedIsInt(in2), &isInt2, &isDouble2);
{
builder_.Bind(&isInt2);
GateRef int2 = builder_.GetInt32OfTInt(in2);
result = BuildIntMinMax<IS_MAX>(int1, int2, in1, in2);
builder_.Jump(&exit);
}
builder_.Bind(&isDouble2);
double1 = builder_.ChangeInt32ToFloat64(int1);
double2 = builder_.GetDoubleOfTDouble(in2);
builder_.Jump(&doubleExit);
}
{
builder_.Bind(&isDouble1);
double1 = builder_.GetDoubleOfTDouble(in1);
double2 = builder_.GetDoubleOfTNumber(in2);
builder_.Jump(&doubleExit);
}
builder_.Bind(&doubleExit);
result = BuildDoubleMinMax<IS_MAX>(*double1, *double2, in1, in2);
builder_.Jump(&exit);
builder_.Bind(&exit);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), *result);
}
template<bool IS_MAX>
void TypedNativeInlineLowering::LowerMathMinMaxWithIntrinsic(GateRef gate)
{
Environment env(gate, circuit_, &builder_);
GateRef in1 = acc_.GetValueIn(gate, 0);
GateRef in2 = acc_.GetValueIn(gate, 1);
GateRef nanValue = builder_.DoubleToTaggedDoublePtr(builder_.NanValue());
DEFVALUE(result, (&builder_), VariableType::JS_ANY(), nanValue);
Label intRes(&builder_);
Label doubleRes(&builder_);
Label exit(&builder_);
builder_.Branch(builder_.BoolAnd(builder_.TaggedIsInt(in1), builder_.TaggedIsInt(in2)), &intRes, &doubleRes);
builder_.Bind(&intRes);
{
GateRef int1 = builder_.GetInt32OfTInt(in1);
GateRef int2 = builder_.GetInt32OfTInt(in2);
GateRef intRet = BuildIntMinMax<IS_MAX>(int1, int2);
result = builder_.Int32ToTaggedPtr(intRet);
builder_.Jump(&exit);
}
builder_.Bind(&doubleRes);
{
GateRef double1 = builder_.GetDoubleOfTNumber(in1);
GateRef double2 = builder_.GetDoubleOfTNumber(in2);
// LLVM supports lowering of `minimum/maximum` intrinsics on X86 only since version 17
// see https://github.com/llvm/llvm-project/commit/a82d27a9a6853c96f857ba0f514a78cd03bc5c35
if (builder_.GetCompilationConfig()->IsAArch64()) {
GateRef doubleRet = IS_MAX ? builder_.DoubleMax(double1, double2) : builder_.DoubleMin(double1, double2);
result = builder_.DoubleToTaggedDoublePtr(doubleRet);
} else {
result = BuildDoubleMinMax<IS_MAX>(double1, double2, in1, in2);
}
builder_.Jump(&exit);
}
builder_.Bind(&exit);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), *result);
}
template<bool IS_MAX>
void TypedNativeInlineLowering::LowerMinMax(GateRef gate)
{
if (isLiteCG_) {
LowerTNumberMinMax<IS_MAX>(gate);
} else {
LowerMathMinMaxWithIntrinsic<IS_MAX>(gate);
}
}
template<bool IS_MAX>
void TypedNativeInlineLowering::LowerIntMinMax(GateRef gate)
{
GateRef in1 = acc_.GetValueIn(gate, 0);
GateRef in2 = acc_.GetValueIn(gate, 1);
Environment env(gate, circuit_, &builder_);
GateRef res = BuildIntMinMax<IS_MAX>(in1, in2);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), res);
}
template<bool IS_MAX>
void TypedNativeInlineLowering::LowerDoubleMinMax(GateRef gate)
{
GateRef in1 = acc_.GetValueIn(gate, 0);
GateRef in2 = acc_.GetValueIn(gate, 1);
Environment env(gate, circuit_, &builder_);
GateRef res = BuildDoubleMinMax<IS_MAX>(in1, in2);
acc_.ReplaceGate(gate, builder_.GetStateDepend(), res);
}
GateRef TypedNativeInlineLowering::FindFrameState(GateRef gate)
{
while (!acc_.HasFrameState(gate)) {
ASSERT(acc_.GetDependCount(gate) > 0);
gate = acc_.GetDep(gate);
}
return acc_.GetFrameState(gate);
}
}

View File

@ -17,17 +17,21 @@
#define ECMASCRIPT_COMPILER_TYPED_NATIVE_INLINE_LOWERING_H
#include "ecmascript/compiler/combined_pass_visitor.h"
#include "ecmascript/compiler/pass_manager.h"
namespace panda::ecmascript::kungfu {
class TypedNativeInlineLowering : public PassVisitor {
public:
TypedNativeInlineLowering(Circuit* circuit,
RPOVisitor* visitor,
PassContext *ctx,
CompilationConfig* cmpCfg,
Chunk* chunk)
: PassVisitor(circuit, chunk, visitor),
circuit_(circuit),
acc_(circuit),
builder_(circuit, cmpCfg) {}
builder_(circuit, cmpCfg),
isLiteCG_(ctx->GetEcmaVM()->GetJSOptions().IsCompilerEnableLiteCG()) {}
~TypedNativeInlineLowering() = default;
GateRef VisitGate(GateRef gate) override;
private:
@ -40,14 +44,42 @@ private:
template <MathTrigonometricCheck CHECK = MathTrigonometricCheck::NOT_NAN>
void LowerGeneralUnaryMath(GateRef gate, RuntimeStubCSigns::ID stubId);
void LowerMathAtan2(GateRef gate);
void LowerAbs(GateRef gate);
void LowerMathPow(GateRef gate);
void LowerMathExp(GateRef gate);
GateRef BuildIntAbs(GateRef value);
GateRef BuildDoubleAbs(GateRef value);
GateRef BuildTNumberAbs(GateRef param);
void LowerAbs(GateRef gate);
void LowerIntAbs(GateRef gate);
void LowerDoubleAbs(GateRef gate);
template<bool IS_MAX>
GateRef BuildIntMinMax(GateRef int1, GateRef int2, GateRef in1, GateRef in2);
template<bool IS_MAX>
GateRef BuildIntMinMax(GateRef in1, GateRef in2);
template<bool IS_MAX>
GateRef BuildDoubleMinMax(GateRef double1, GateRef double2, GateRef in1, GateRef in2);
template<bool IS_MAX>
GateRef BuildDoubleMinMax(GateRef in1, GateRef in2);
template<bool IS_MAX>
void LowerTNumberMinMax(GateRef gate);
template<bool IS_MAX>
void LowerMathMinMaxWithIntrinsic(GateRef gate);
template<bool IS_MAX>
void LowerMinMax(GateRef gate);
template<bool IS_MAX>
void LowerIntMinMax(GateRef gate);
template<bool IS_MAX>
void LowerDoubleMinMax(GateRef gate);
GateRef FindFrameState(GateRef gate);
private:
Circuit* circuit_ {nullptr};
GateAccessor acc_;
CircuitBuilder builder_;
bool isLiteCG_ {false};
};
}
#endif // ECMASCRIPT_COMPILER_TYPED_HCR_LOWERING_H

View File

@ -178,6 +178,8 @@ class ObjectFactory;
V(JSTaggedValue, MathPow, MATH_POW_INDEX, ecma_roots_special) \
V(JSTaggedValue, MathCbrt, MATH_CBRT_INDEX, ecma_roots_special) \
V(JSTaggedValue, MathFloorFunction, MATH_FLOOR_FUNCTION_INDEX, ecma_roots_special) \
V(JSTaggedValue, MathMin, MATH_MIN_INDEX, ecma_roots_special) \
V(JSTaggedValue, MathMax, MATH_MAX_INDEX, ecma_roots_special) \
V(JSTaggedValue, LocaleCompareFunction, LOCALE_COMPARE_FUNCTION_INDEX, ecma_roots_special) \
V(JSTaggedValue, ArraySortFunction, ARRAY_SORT_FUNCTION_INDEX, ecma_roots_special) \
V(JSTaggedValue, JsonStringifyFunction, JSON_STRINGIFY_FUNCTION_INDEX, ecma_roots_special) \

View File

@ -1,3 +1,4 @@
#!/bin/bash
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -11,42 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
NaN
0
3
5
1.5
3.141592653589793
3.141592653589793
1.9e+80
1.9e+80
1.9e-80
1.9e-80
Infinity
Infinity
NaN
1/x: Infinity
3
3
4
4
-1.001
2147483647
2147483648
2147483647
2147483648
2147483649
3
NaN
NaN
12
NaN
12
12
NaN
12
NaN
23
14
Error: already positive
23
MODE=$1
if [ $# -ne 3 ] || ([ $MODE != "pgo" ] && [ $MODE != "aot" ]); then
echo "Usage: $0 [pgo|aot] <inputs.ts> <expect_output.txt>"
exit 1
fi
INPUT=$2
OUTPUT=$3
# copy license from this file
tail -n +2 $0 | head -n 13 > $OUTPUT
grep -Po "//($MODE)?: \K.*$" $INPUT >> $OUTPUT

View File

@ -20,5 +20,6 @@ host_aot_test_action("builtinMathAbs") {
is_enable_trace_deopt = true
is_enable_enableArkTools = true
log_option = " --log-info=trace"
gen_expect_output = true
deps = []
}

View File

@ -33,84 +33,103 @@ function printAbs(x: any) {
}
}
function printAbs2() {
try {
const INT_MAX: number = 2147483647;
const INT_MIN: number = -INT_MAX - 1;
print(Math.abs(INT_MIN));
} finally {
}
}
// Check without params
print(Math.abs()); // NaN
print(Math.abs()); //: NaN
// Check with single int param
print(Math.abs(0)); // 0
print(Math.abs(3)); // 3
print(Math.abs(-5)); // 5
print(Math.abs(0)); //: 0
print(Math.abs(3)); //: 3
print(Math.abs(-5)); //: 5
// Check with single float param
print(Math.abs(-1.5));
print(Math.abs(Math.PI));
print(Math.abs(-Math.PI));
print(Math.abs(-1.9e80));
print(Math.abs(1.9e80));
print(Math.abs(-1.9e-80));
print(Math.abs(1.9e-80));
print(Math.abs(-1.5)); //: 1.5
print(Math.abs(Math.PI)); //: 3.141592653589793
print(Math.abs(-Math.PI)); //: 3.141592653589793
print(Math.abs(-1.9e80)); //: 1.9e+80
print(Math.abs(1.9e80)); //: 1.9e+80
print(Math.abs(-1.9e-80)); //: 1.9e-80
print(Math.abs(1.9e-80)); //: 1.9e-80
// Check with special float params
print(Math.abs(Infinity)); // Infinity
print(Math.abs(-Infinity)); // Infinity
print(Math.abs(NaN)); // NaN
print(Math.abs(Infinity)); //: Infinity
print(Math.abs(-Infinity)); //: Infinity
print(Math.abs(NaN)); //: NaN
print("1/x: " + 1 / Math.abs(-0));
print(1 / Math.abs(-0)); //: Infinity
// Check with 2 params
print(Math.abs(3, 0)); // 3
print(Math.abs(3, 0)); //: 3
// Check with 3 params
print(Math.abs(-3, 0, 0)); // 3
print(Math.abs(-3, 0, 0)); //: 3
// Check with 4 params
print(Math.abs(4, 0, 0, 0)); // 4
print(Math.abs(4, 0, 0, 0)); //: 4
// Check with 5 params
print(Math.abs(-4, 0, 0, 0, 0)); // 4
print(Math.abs(-4, 0, 0, 0, 0)); //: 4
// Replace standard builtin
let true_abs = Math.abs
Math.abs = replace
print(Math.abs(-1.001)); // -1.001, no deopt
// no deopt
print(Math.abs(-1.001)); //: -1.001
Math.abs = true_abs
// Check edge cases
const INT_MAX: number = 2147483647;
const INT_MIN: number = -INT_MAX - 1;
print(Math.abs(INT_MAX)); // INT_MAX
print(Math.abs(2147483648)); // INT_MAX + 1
print(Math.abs(-INT_MAX)); // INT_MAX
print(Math.abs(INT_MIN)); // -INT_MIN
print(Math.abs(INT_MIN - 1)); // -INT_MIN + 1
print(Math.abs(INT_MAX)); //: 2147483647
print(Math.abs(2147483648)); //: 2147483648
print(Math.abs(-INT_MAX)); //: 2147483647
//aot: [trace] Check Type: NotInt3
printAbs2(); //: 2147483648
print(Math.abs(INT_MIN - 1)); //: 2147483649
printAbs(-3);
printAbs("abc");
printAbs("abc");
printAbs(-12); // 12
printAbs(-12); //: 12
// Call standard builtin with non-number param
printAbs("abc"); // NaN
printAbs("-12"); // 12
//aot: [trace] Check Type: NotNumber2
printAbs("abc"); //: NaN
//aot: [trace] Check Type: NotNumber2
printAbs("-12"); //: 12
if (ArkTools.isAOTCompiled(printAbs)) {
// Replace standard builtin after call to standard builtin was profiled
Math.abs = replace
}
printAbs(-12); // 12; or -12, deopt
printAbs("abc"); // NaN; or abc, deopt
printAbs(-12); //pgo: 12
//aot: [trace] Check Type: NotCallTarget1
//aot: -12
printAbs("abc"); //pgo: NaN
//aot: [trace] Check Type: NotCallTarget1
//aot: abc
Math.abs = true_abs
// Check IR correctness inside try-block
try {
printAbs(-12); // 12
printAbs("abc"); // NaN
printAbs(-12); //: 12
//aot: [trace] Check Type: NotNumber2
printAbs("abc"); //: NaN
} catch (e) {
}
let obj = {};
obj.valueOf = (() => { return -23; })
print(Math.abs(obj)); // 23
let obj = {
valueOf: () => { return -23; }
};
//aot: [trace] Check Type: NotNumber2
print(Math.abs(obj)); //: 23
function Throwing() {
this.value = -14;
@ -124,11 +143,11 @@ Throwing.prototype.valueOf = function() {
let throwingObj = new Throwing();
try {
print(Math.abs(throwingObj)); // 14
print(Math.abs(throwingObj)); //: 14
throwingObj.value = 10;
print(Math.abs(throwingObj)); // exception
print(Math.abs(throwingObj)); //: Error: already positive
} catch(e) {
print(e);
} finally {
print(Math.abs(obj)); // 23
print(Math.abs(obj)); //: 23
}

View File

@ -36,6 +36,8 @@ group("ark_aot_builtin_inlining_math_test") {
"Abs",
"Pow",
"Cbrt",
"Min",
"Max",
]
deps = []

View File

@ -11,50 +11,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
NaN
0
3
5
1.5
3.141592653589793
3.141592653589793
1.9e+80
1.9e+80
1.9e-80
1.9e-80
Infinity
Infinity
NaN
1/x: Infinity
3
3
4
4
-1.001
2147483647
2147483648
2147483647
2147483648
2147483649
3
[trace] Check Type: NotNumber2
NaN
[trace] Check Type: NotNumber2
NaN
12
[trace] Check Type: NotNumber2
NaN
[trace] Check Type: NotNumber2
12
[trace] Check Type: NotCallTarget1
-12
[trace] Check Type: NotCallTarget1
abc
12
[trace] Check Type: NotNumber2
NaN
[trace] Check Type: NotNumber2
23
14
Error: already positive
23
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_aot_test_action("builtinMathMax") {
is_enable_pgo = true
is_only_typed_path = true
is_enable_opt_inlining = true
is_enable_trace_deopt = true
is_enable_enableArkTools = true
gen_expect_output = true
log_option = " --log-info=trace"
deps = []
}

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare interface ArkTools {
isAOTCompiled(args: any): boolean;
}
declare function print(arg:any):string;
function printZero(x: any) {
if (Object.is(x, -0)) {
print("-0");
} else {
print(x);
}
}
function replace(a : number)
{
return a;
}
// Use try to prevent inlining to main
function printMax(x: any, y: any) {
try {
print(Math.max(x, y));
} finally {
}
}
function printMax1(x: any) {
try {
print(Math.max(x));
} finally {
}
}
function printMax3(x: any, y: any, z: any) {
try {
print(Math.max(x, y, z));
} finally {
}
}
let doubleObj = {
valueOf: () => { return 2.7; }
}
let nanObj = {
valueOf: () => { return "something"; }
}
let obj = {
valueOf: () => {
print("obj.valueOf")
return -23;
}
};
// Check without params
print(Math.max()); //: -Infinity
// Check with single param
print(Math.max(0)); //: 0
print(Math.max(567)); //: 567
print(Math.max(-1e80)); //: -1e+80
// Check with special float params
print(Math.max(Infinity)); //: Infinity
print(Math.max(-Infinity)); //: -Infinity
print(Math.max(NaN)); //: NaN
// Check with 2 int params
print(Math.max(3, 300)); //: 300
print(Math.max(300, 3)); //: 300
print(Math.max(3, -2)); //: 3
print(Math.max(-22, -2)); //: -2
print(Math.max(-1, -1)); //: -1
// Check with 2 float params
print(Math.max(3.2, 300.7)); //: 300.7
print(Math.max(300.7, 3.2)); //: 300.7
print(Math.max(3e9, -2e10)); //: 3000000000
print(Math.max(-22.1, -2e-10)); //: -2e-10
print(Math.max(-1.8, -1.8)); //: -1.8
print(Math.max(Infinity, -1.8)); //: Infinity
print(Math.max(-1.8, Infinity)); //: Infinity
print(Math.max(-Infinity, -1.8)); //: -1.8
print(Math.max(-1.8, -Infinity)); //: -1.8
print(Math.max(-1.8, NaN)); //: NaN
print(Math.max(NaN, -1.8)); //: NaN
print(Math.max(-Infinity, NaN)); //: NaN
print(Math.max(Infinity, NaN)); //: NaN
// Check 0 and -0
printZero(Math.max(0, -0)); //: 0
printZero(Math.max(-0, 0)); //: 0
printZero(Math.max(0, 0)); //: 0
printZero(Math.max(-0, -0)); //: -0
printZero(Math.max(-5, -0)); //: -0
printZero(Math.max(-5, 0)); //: 0
// Check with int and float param
print(Math.max(1.5, 3)); //: 3
print(Math.max(3, 1.5)); //: 3
print(Math.max(2.5, 1)); //: 2.5
print(Math.max(1, 2.5)); //: 2.5
print(Math.max(1, -Infinity)); //: 1
print(Math.max(5, NaN)); //: NaN
print(Math.max(NaN, 5)); //: NaN
// Check with 3 params
print(Math.max(2, 4, 6)); //: 6
print(Math.max(4, 6, 2)); //: 6
print(Math.max(6, 2, 4)); //: 6
print(Math.max(-1, 1, 2.5)); //: 2.5
// Check with 4 params
print(Math.max(-4, 0, 2, -Infinity)); //: 2
// Check with 5 params
print(Math.max(1, 2, 1.5, 2, 8)); //: 8
// Replace standard builtin
let trueMax = Math.max
Math.max = replace
print(Math.max(-1.001, -90)); //: -1.001
Math.max = trueMax
printMax(-12, -100); //: -12
// Call standard builtin with non-number param
//aot: [trace] Check Type: NotNumber2
printMax("abc", -100); //: NaN
//aot: [trace] Check Type: NotNumber2
printMax("-12", -100); //: -12
if (ArkTools.isAOTCompiled(printMax)) {
// Replace standard builtin after call to standard builtin was profiled
Math.max = replace
}
//aot: [trace] Check Type: NotCallTarget1
printMax(5, 12); //pgo: 12
//aot: 5
//aot: [trace] Check Type: NotCallTarget1
printMax("abc", 2); //pgo: NaN
//aot: abc
Math.max = trueMax
// Check IR correctness inside try-block
try {
print(Math.max(19)); //: 19
print(Math.max(19, 20)); //: 20
printMax(19, 12); //: 19
//aot: [trace] Check Type: NotNumber2
printMax("abc", 5); //: NaN
} catch (e) {
}
//aot: [trace] Check Type: BuiltinInliningTypeGuard
//: obj.valueOf
printMax1(obj); //: -23
//aot: [trace] Check Type: BuiltinInliningTypeGuard
printMax1(doubleObj); //: 2.7
//aot: [trace] Check Type: NotNumber2
printMax(doubleObj, doubleObj); //: 2.7
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
printMax(nanObj, obj); //: NaN
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
printMax(2, obj); //: 2
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
printMax(-100, obj); //: -23
// call obj.valueOf twice
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
//: obj.valueOf
printMax3(NaN, obj, obj);
// print(Math.max(NaN, obj, obj)); //: NaN

View File

@ -0,0 +1,25 @@
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_aot_test_action("builtinMathMin") {
is_enable_pgo = true
is_only_typed_path = true
is_enable_opt_inlining = true
is_enable_trace_deopt = true
is_enable_enableArkTools = true
gen_expect_output = true
log_option = " --log-info=trace"
deps = []
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare interface ArkTools {
isAOTCompiled(args: any): boolean;
}
declare function print(arg:any):string;
function printZero(x: any) {
if (Object.is(x, -0)) {
print("-0");
} else {
print(x);
}
}
function replace(a : number)
{
return a;
}
// Use try to prevent inlining to main
function printMin(x: any, y: any) {
try {
print(Math.min(x, y));
} finally {
}
}
function printMin1(x: any) {
try {
print(Math.min(x));
} finally {
}
}
let doubleObj = {
valueOf: () => { return 2.7; }
}
let nanObj = {
valueOf: () => { return "something"; }
}
let obj = {
valueOf: () => {
print("obj.valueOf")
return -23;
}
};
// Check without params
print(Math.min()); //: Infinity
// Check with single param
print(Math.min(0)); //: 0
print(Math.min(567)); //: 567
print(Math.min(-1e80)); //: -1e+80
// Check with special float params
print(Math.min(Infinity)); //: Infinity
print(Math.min(-Infinity)); //: -Infinity
print(Math.min(NaN)); //: NaN
// Check with 2 int params
print(Math.min(3, 300)); //: 3
print(Math.min(300, 3)); //: 3
print(Math.min(3, -2)); //: -2
print(Math.min(-22, -2)); //: -22
print(Math.min(-1, -1)); //: -1
// // Check with 2 float params
print(Math.min(3.2, 300.7)); //: 3.2
print(Math.min(300.7, 3.2)); //: 3.2
print(Math.min(3e11, -2e10)); //: -20000000000
print(Math.min(-22.1, -2e-10)); //: -22.1
print(Math.min(-1.8, -1.8)); //: -1.8
print(Math.min(Infinity, -1.8)); //: -1.8
print(Math.min(-1.8, Infinity)); //: -1.8
print(Math.min(-Infinity, -1.8)); //: -Infinity
print(Math.min(-1.8, -Infinity)); //: -Infinity
print(Math.min(-1.8, NaN)); //: NaN
print(Math.min(NaN, -1.8)); //: NaN
print(Math.min(-Infinity, NaN)); //: NaN
print(Math.min(Infinity, NaN)); //: NaN
// Check 0 and -0
printZero(Math.min(0, -0)); //: -0
printZero(Math.min(-0, 0)); //: -0
printZero(Math.min(0, 0)); //: 0
printZero(Math.min(-0, -0)); //: -0
printZero(Math.min(5, -0)); //: -0
printZero(Math.min(5, 0)); //: 0
// Check with int and float param
print(Math.min(1.5, 3)); //: 1.5
print(Math.min(3, 1.5)); //: 1.5
print(Math.min(2.5, 1)); //: 1
print(Math.min(1, 2.5)); //: 1
print(Math.min(1, Infinity)); //: 1
print(Math.min(5, NaN)); //: NaN
print(Math.min(NaN, 5)); //: NaN
// Check with 3 params
print(Math.min(2, 4, 6)); //: 2
print(Math.min(4, 6, 2)); //: 2
print(Math.min(6, 2, 4)); //: 2
print(Math.min(-1, 1, -2.5)); //: -2.5
// Check with 4 params
print(Math.min(4, 0, -2, Infinity)); //: -2
// Check with 5 params
print(Math.min(-1, -2, -1.5, -2, -8)); //: -8
// Replace standard builtin
let trueMin = Math.min
Math.min = replace
print(Math.min(-1.001, -90)); //: -1.001
Math.min = trueMin
printMin(-12, 1); //: -12
// Call standard builtin with non-number param
//aot: [trace] Check Type: NotNumber2
printMin("abc", 1); //: NaN
//aot: [trace] Check Type: NotNumber2
printMin("-12", 1); //: -12
if (ArkTools.isAOTCompiled(printMin)) {
// Replace standard builtin after call to standard builtin was profiled
Math.min = replace
}
//aot: [trace] Check Type: NotCallTarget1
printMin(5, -12); //pgo: -12
//aot: 5
//aot: [trace] Check Type: NotCallTarget1
printMin("abc", 2); //pgo: NaN
//aot: abc
Math.min = trueMin
// Check IR correctness inside try-block
try {
printMin(19, 12); //: 12
//aot: [trace] Check Type: NotNumber2
printMin("abc", 5); //: NaN
} catch (e) {
}
//aot: [trace] Check Type: BuiltinInliningTypeGuard
//: obj.valueOf
printMin1(obj); //: -23
//aot: [trace] Check Type: BuiltinInliningTypeGuard
printMin1(doubleObj); //: 2.7
//aot: [trace] Check Type: NotNumber2
printMin(doubleObj, doubleObj); //: 2.7
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
printMin(nanObj, obj); //: NaN
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
printMin(24, obj); //: -23
// call obj.valueOf twice
//aot: [trace] Check Type: NotNumber2
//: obj.valueOf
//: obj.valueOf
print(Math.min(NaN, obj, obj)); //: NaN

View File

@ -2219,6 +2219,7 @@ template("host_aot_test_action") {
_test_aot_path_ = "$target_out_dir/${_target_name_}.an"
_test_aot_snapshot_path_ = "$target_out_dir/${_target_name_}.ai"
_test_aot_arg_ = "$target_out_dir/${_target_name_}"
_test_profiler_path_ = "${_test_aot_arg_}/modules.ap"
_test_aot_path_slowpath_ = "$target_out_dir/slowpath/${_target_name_}.an"
_test_aot_snapshot_path_slowpath_ =
"$target_out_dir/slowpath/${_target_name_}.ai"
@ -2249,6 +2250,29 @@ template("host_aot_test_action") {
"$target_out_dir/slowpath/litecg/${_target_name_}"
}
if (defined(invoker.gen_expect_output) && invoker.gen_expect_output) {
_test_expect_path_ = "$target_out_dir/expect_output.txt"
_test_pgo_expect_path_ = "$target_out_dir/pgo_expect_output.txt"
action("${_target_name_}GenExpectOutput") {
script = "$js_root/script/gen_expect_output.sh"
sources = [ _test_ts_path_ ]
outputs = [ _test_expect_path_ ]
args = [ "aot" ] + rebase_path(sources + outputs)
}
action("${_target_name_}PgoGenExpectOutput") {
script = "$js_root/script/gen_expect_output.sh"
sources = [ _test_ts_path_ ]
outputs = [ _test_pgo_expect_path_ ]
args = [ "pgo" ] + rebase_path(sources + outputs)
}
_deps_ += [
":${_target_name_}GenExpectOutput",
":${_target_name_}PgoGenExpectOutput",
]
}
es2abc_gen_abc("gen_${_target_name_}_abc") {
extra_visibility = [ ":*" ] # Only targets in this file can depend on this.
extra_dependencies = _deps_
@ -2298,7 +2322,7 @@ template("host_aot_test_action") {
_aot_run_options_ =
" --asm-interpreter=true" + " --entry-point=${_target_name_}" +
" --enable-pgo-profiler=true" + " --compiler-pgo-profiler-path=" +
rebase_path(_test_aot_arg_) + "/modules.ap"
rebase_path(_test_profiler_path_)
if (defined(invoker.is_enable_enableArkTools) &&
invoker.is_enable_enableArkTools) {
@ -2331,7 +2355,7 @@ template("host_aot_test_action") {
inputs = [ _test_abc_path_ ]
outputs = [ "$target_out_dir/${_target_name_}/pgo" ]
outputs = [ _test_profiler_path_ ]
}
action("${_target_name_}AotCompileAction") {
@ -2357,8 +2381,8 @@ template("host_aot_test_action") {
" --compiler-opt-inlining=false" + " --compiler-opt-loop-peeling=false"
if (defined(invoker.is_enable_pgo) && invoker.is_enable_pgo) {
_aot_compile_options_ += " --compiler-pgo-profiler-path=" +
rebase_path(_test_aot_arg_) + "/modules.ap"
_aot_compile_options_ +=
" --compiler-pgo-profiler-path=" + rebase_path(_test_profiler_path_)
}
if (defined(invoker.is_enable_trace_deopt) &&
invoker.is_enable_trace_deopt) {
@ -2771,8 +2795,8 @@ template("host_aot_test_action") {
" --compiler-enable-litecg=true"
if (defined(invoker.is_enable_pgo) && invoker.is_enable_pgo) {
_aot_compile_options_ += " --compiler-pgo-profiler-path=" +
rebase_path(_test_aot_arg_) + "/modules.ap"
_aot_compile_options_ +=
" --compiler-pgo-profiler-path=" + rebase_path(_test_profiler_path_)
}
if (defined(invoker.is_enable_trace_deopt) &&
invoker.is_enable_trace_deopt) {
@ -3180,8 +3204,8 @@ template("host_aot_test_action") {
" --compiler-target-triple=aarch64-unknown-linux-gnu"
if (defined(invoker.is_enable_pgo) && invoker.is_enable_pgo) {
_aot_compile_options_ += " --compiler-pgo-profiler-path=" +
rebase_path(_test_aot_arg_) + "/modules.ap"
_aot_compile_options_ +=
" --compiler-pgo-profiler-path=" + rebase_path(_test_profiler_path_)
}
if (defined(invoker.is_enable_trace_deopt) &&
invoker.is_enable_trace_deopt) {