Commit 3cfb9308 authored by Milad Fa's avatar Milad Fa Committed by V8 LUCI CQ

PPC/s390: [masm] Improve Instance Type Checks in Builtins::Call/Construct

Port b9a6301e

Original Commit Message:

    Load instance type into a register instead of using memory operands for
    several checks on ia32 and x64.

R=pthier@chromium.org, joransiu@ca.ibm.com, junyan@redhat.com, midawson@redhat.com
BUG=
LOG=N

Change-Id: I05ea2bd32ea2a2053b601323813c580d55094e46
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3198130Reviewed-by: 's avatarJunliang Yan <junyan@redhat.com>
Commit-Queue: Milad Fa <mfarazma@redhat.com>
Cr-Commit-Position: refs/heads/main@{#77179}
parent 25f0e329
......@@ -2231,38 +2231,48 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- r3 : the number of arguments (not including the receiver)
// -- r4 : the target to call (can be any Object).
// -----------------------------------
Register argc = r3;
Register target = r4;
Register map = r7;
Register instance_type = r8;
DCHECK(!AreAliased(argc, target, map, instance_type));
Label non_callable, class_constructor;
__ JumpIfSmi(r4, &non_callable);
__ LoadMap(r7, r4);
__ CompareInstanceTypeRange(r7, r8, FIRST_CALLABLE_JS_FUNCTION_TYPE,
__ JumpIfSmi(target, &non_callable);
__ LoadMap(map, target);
__ CompareInstanceTypeRange(map, instance_type,
FIRST_CALLABLE_JS_FUNCTION_TYPE,
LAST_CALLABLE_JS_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
RelocInfo::CODE_TARGET, le);
__ cmpi(r8, Operand(JS_BOUND_FUNCTION_TYPE));
__ cmpi(instance_type, Operand(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Check if target has a [[Call]] internal method.
__ lbz(r7, FieldMemOperand(r7, Map::kBitFieldOffset));
__ TestBit(r7, Map::Bits1::IsCallableBit::kShift, r0);
__ beq(&non_callable, cr0);
{
Register flags = r7;
__ lbz(flags, FieldMemOperand(map, Map::kBitFieldOffset));
map = no_reg;
__ TestBit(flags, Map::Bits1::IsCallableBit::kShift, r0);
__ beq(&non_callable, cr0);
}
// Check if target is a proxy and call CallProxy external builtin
__ cmpi(r8, Operand(JS_PROXY_TYPE));
__ cmpi(instance_type, Operand(JS_PROXY_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET, eq);
// ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
// Check that the function is not a "classConstructor".
__ cmpi(r8, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ cmpi(instance_type, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ beq(&class_constructor);
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
// Overwrite the original receiver the (original) target.
__ StoreReceiver(r4, r3, r8);
__ StoreReceiver(target, argc, r8);
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(r4, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2271,7 +2281,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r4);
__ Push(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
}
......@@ -2279,7 +2289,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r4);
__ Push(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Trap(); // Unreachable.
}
......@@ -2352,32 +2362,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// -- r6 : the new target (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -----------------------------------
Register argc = r3;
Register target = r4;
Register map = r7;
Register instance_type = r8;
DCHECK(!AreAliased(argc, target, map, instance_type));
// Check if target is a Smi.
Label non_constructor, non_proxy;
__ JumpIfSmi(r4, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ LoadTaggedPointerField(r7, FieldMemOperand(r4, HeapObject::kMapOffset),
r0);
__ lbz(r5, FieldMemOperand(r7, Map::kBitFieldOffset));
__ TestBit(r5, Map::Bits1::IsConstructorBit::kShift, r0);
__ beq(&non_constructor, cr0);
__ LoadTaggedPointerField(
map, FieldMemOperand(target, HeapObject::kMapOffset), r0);
{
Register flags = r5;
DCHECK(!AreAliased(argc, target, map, instance_type, flags));
__ lbz(flags, FieldMemOperand(map, Map::kBitFieldOffset));
__ TestBit(flags, Map::Bits1::IsConstructorBit::kShift, r0);
__ beq(&non_constructor, cr0);
}
// Dispatch based on instance type.
__ CompareInstanceTypeRange(r7, r8, FIRST_JS_FUNCTION_TYPE,
__ CompareInstanceTypeRange(map, instance_type, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction),
RelocInfo::CODE_TARGET, le);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ cmpi(r8, Operand(JS_BOUND_FUNCTION_TYPE));
__ cmpi(instance_type, Operand(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Only dispatch to proxies after checking whether they are constructors.
__ cmpi(r8, Operand(JS_PROXY_TYPE));
__ cmpi(instance_type, Operand(JS_PROXY_TYPE));
__ bne(&non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy),
RelocInfo::CODE_TARGET);
......@@ -2386,9 +2405,10 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ bind(&non_proxy);
{
// Overwrite the original receiver with the (original) target.
__ StoreReceiver(r4, r3, r8);
__ StoreReceiver(target, argc, r8);
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(r4, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -2273,38 +2273,48 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- r2 : the number of arguments (not including the receiver)
// -- r3 : the target to call (can be any Object).
// -----------------------------------
Register argc = r2;
Register target = r3;
Register map = r6;
Register instance_type = r7;
DCHECK(!AreAliased(argc, target, map, instance_type));
Label non_callable, class_constructor;
__ JumpIfSmi(r3, &non_callable);
__ LoadMap(r6, r3);
__ CompareInstanceTypeRange(r6, r7, FIRST_CALLABLE_JS_FUNCTION_TYPE,
__ JumpIfSmi(target, &non_callable);
__ LoadMap(map, target);
__ CompareInstanceTypeRange(map, instance_type,
FIRST_CALLABLE_JS_FUNCTION_TYPE,
LAST_CALLABLE_JS_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
RelocInfo::CODE_TARGET, le);
__ CmpS64(r7, Operand(JS_BOUND_FUNCTION_TYPE));
__ CmpS64(instance_type, Operand(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Check if target has a [[Call]] internal method.
__ LoadU8(r6, FieldMemOperand(r6, Map::kBitFieldOffset));
__ TestBit(r6, Map::Bits1::IsCallableBit::kShift);
__ beq(&non_callable);
{
Register flags = r6;
__ LoadU8(flags, FieldMemOperand(map, Map::kBitFieldOffset));
map = no_reg;
__ TestBit(flags, Map::Bits1::IsCallableBit::kShift);
__ beq(&non_callable);
}
// Check if target is a proxy and call CallProxy external builtin
__ CmpS64(r7, Operand(JS_PROXY_TYPE));
__ CmpS64(instance_type, Operand(JS_PROXY_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET, eq);
// ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
// Check that the function is not a "classConstructor".
__ CmpS64(r7, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ CmpS64(instance_type, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ beq(&class_constructor);
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
// Overwrite the original receiver the (original) target.
__ StoreReceiver(r3, r2, r7);
__ StoreReceiver(target, argc, r7);
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(r3, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2313,7 +2323,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r3);
__ Push(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
__ Trap(); // Unreachable.
}
......@@ -2322,7 +2332,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r3);
__ Push(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Trap(); // Unreachable.
}
......@@ -2394,31 +2404,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// -- r5 : the new target (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -----------------------------------
Register argc = r2;
Register target = r3;
Register map = r6;
Register instance_type = r7;
DCHECK(!AreAliased(argc, target, map, instance_type));
// Check if target is a Smi.
Label non_constructor, non_proxy;
__ JumpIfSmi(r3, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ LoadTaggedPointerField(r6, FieldMemOperand(r3, HeapObject::kMapOffset));
__ LoadU8(r4, FieldMemOperand(r6, Map::kBitFieldOffset));
__ TestBit(r4, Map::Bits1::IsConstructorBit::kShift);
__ beq(&non_constructor);
__ LoadTaggedPointerField(map,
FieldMemOperand(target, HeapObject::kMapOffset));
{
Register flags = r4;
DCHECK(!AreAliased(argc, target, map, instance_type, flags));
__ LoadU8(flags, FieldMemOperand(map, Map::kBitFieldOffset));
__ TestBit(flags, Map::Bits1::IsConstructorBit::kShift);
__ beq(&non_constructor);
}
// Dispatch based on instance type.
__ CompareInstanceTypeRange(r6, r7, FIRST_JS_FUNCTION_TYPE,
__ CompareInstanceTypeRange(map, instance_type, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction),
RelocInfo::CODE_TARGET, le);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ CmpS64(r7, Operand(JS_BOUND_FUNCTION_TYPE));
__ CmpS64(instance_type, Operand(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Only dispatch to proxies after checking whether they are constructors.
__ CmpS64(r7, Operand(JS_PROXY_TYPE));
__ CmpS64(instance_type, Operand(JS_PROXY_TYPE));
__ bne(&non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy),
RelocInfo::CODE_TARGET);
......@@ -2427,9 +2447,10 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ bind(&non_proxy);
{
// Overwrite the original receiver with the (original) target.
__ StoreReceiver(r3, r2, r7);
__ StoreReceiver(target, argc, r7);
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(r3, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -1704,16 +1704,28 @@ void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
cmpi(type_reg, Operand(type));
}
void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
if (lower_limit != 0) {
mov(scratch, Operand(lower_limit));
sub(scratch, value, scratch);
cmpli(scratch, Operand(higher_limit - lower_limit));
} else {
mov(scratch, Operand(higher_limit));
CmpU64(value, scratch);
}
}
void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
InstanceType lower_limit,
InstanceType higher_limit) {
DCHECK_LT(lower_limit, higher_limit);
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
LoadU16(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
mov(scratch, Operand(lower_limit));
sub(scratch, type_reg, scratch);
cmpli(scratch, Operand(higher_limit - lower_limit));
CompareRange(type_reg, lower_limit, higher_limit);
}
void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
......@@ -1900,15 +1912,7 @@ void TurboAssembler::MaxF64(DoubleRegister dst, DoubleRegister lhs,
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit,
Label* on_in_range) {
Register scratch = r0;
if (lower_limit != 0) {
mov(scratch, Operand(lower_limit));
sub(scratch, value, scratch);
cmpli(scratch, Operand(higher_limit - lower_limit));
} else {
mov(scratch, Operand(higher_limit));
CmpU64(value, scratch);
}
CompareRange(value, lower_limit, higher_limit);
ble(on_in_range);
}
......
......@@ -1206,6 +1206,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Checks if value is in range [lower_limit, higher_limit] using a single
// comparison.
void CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit);
void JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range);
......
......@@ -1891,16 +1891,27 @@ void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
CmpS64(type_reg, Operand(type));
}
void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
if (lower_limit != 0) {
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
mov(scratch, value);
slgfi(scratch, Operand(lower_limit));
CmpU64(scratch, Operand(higher_limit - lower_limit));
} else {
CmpU64(value, Operand(higher_limit));
}
}
void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
InstanceType lower_limit,
InstanceType higher_limit) {
DCHECK_LT(lower_limit, higher_limit);
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
LoadU16(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
mov(scratch, type_reg);
slgfi(scratch, Operand(lower_limit));
CmpU64(scratch, Operand(higher_limit - lower_limit));
CompareRange(type_reg, lower_limit, higher_limit);
}
void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
......@@ -1914,14 +1925,7 @@ void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit,
Label* on_in_range) {
if (lower_limit != 0) {
Register scratch = r0;
mov(scratch, value);
slgfi(scratch, Operand(lower_limit));
CmpU64(scratch, Operand(higher_limit - lower_limit));
} else {
CmpU64(value, Operand(higher_limit));
}
CompareRange(value, lower_limit, higher_limit);
ble(on_in_range);
}
......
......@@ -1365,6 +1365,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Checks if value is in range [lower_limit, higher_limit] using a single
// comparison.
void CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit);
void JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment