Commit b9a6301e authored by Patrick Thier's avatar Patrick Thier Committed by V8 LUCI CQ

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

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

Drive-by: Name used registers in Generate_Call/Generate_Construct

Change-Id: I289c5e420fa03ca639c9b78266560cafb166f6f7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3190099Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77156}
parent c5c60391
......@@ -2466,38 +2466,48 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- r0 : the number of arguments
// -- r1 : the target to call (can be any Object).
// -----------------------------------
Register argc = r0;
Register target = r1;
Register map = r4;
Register instance_type = r5;
DCHECK(!AreAliased(argc, target, map, instance_type));
Label non_callable, class_constructor;
__ JumpIfSmi(r1, &non_callable);
__ LoadMap(r4, r1);
__ CompareInstanceTypeRange(r4, r5, 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, ls);
__ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE));
__ cmp(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.
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r4, Operand(Map::Bits1::IsCallableBit::kMask));
__ b(eq, &non_callable);
{
Register flags = r4;
__ ldrb(flags, FieldMemOperand(map, Map::kBitFieldOffset));
map = no_reg;
__ tst(flags, Operand(Map::Bits1::IsCallableBit::kMask));
__ b(eq, &non_callable);
}
// Check if target is a proxy and call CallProxy external builtin
__ cmp(r5, Operand(JS_PROXY_TYPE));
__ cmp(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".
__ cmp(r5, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ cmp(instance_type, Operand(JS_CLASS_CONSTRUCTOR_TYPE));
__ b(eq, &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.
__ str(r1, __ ReceiverOperand(r0));
__ str(target, __ ReceiverOperand(argc));
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(r1, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2506,7 +2516,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r1);
__ Push(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
__ Trap(); // Unreachable.
}
......@@ -2515,7 +2525,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r1);
__ Push(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Trap(); // Unreachable.
}
......@@ -2582,31 +2592,40 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// -- r3 : the new target (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -----------------------------------
Register argc = r0;
Register target = r1;
Register map = r4;
Register instance_type = r5;
DCHECK(!AreAliased(argc, target, map, instance_type));
// Check if target is a Smi.
Label non_constructor, non_proxy;
__ JumpIfSmi(r1, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r2, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r2, Operand(Map::Bits1::IsConstructorBit::kMask));
__ b(eq, &non_constructor);
__ ldr(map, FieldMemOperand(target, HeapObject::kMapOffset));
{
Register flags = r2;
DCHECK(!AreAliased(argc, target, map, instance_type, flags));
__ ldrb(flags, FieldMemOperand(map, Map::kBitFieldOffset));
__ tst(flags, Operand(Map::Bits1::IsConstructorBit::kMask));
__ b(eq, &non_constructor);
}
// Dispatch based on instance type.
__ CompareInstanceTypeRange(r4, r5, 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, ls);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE));
__ cmp(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.
__ cmp(r5, Operand(JS_PROXY_TYPE));
__ cmp(instance_type, Operand(JS_PROXY_TYPE));
__ b(ne, &non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy),
RelocInfo::CODE_TARGET);
......@@ -2615,9 +2634,10 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ bind(&non_proxy);
{
// Overwrite the original receiver with the (original) target.
__ str(r1, __ ReceiverOperand(r0));
__ str(target, __ ReceiverOperand(argc));
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(r1, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -2891,39 +2891,49 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- x0 : the number of arguments
// -- x1 : the target to call (can be any Object).
// -----------------------------------
Register argc = x0;
Register target = x1;
Register map = x4;
Register instance_type = x5;
DCHECK(!AreAliased(argc, target, map, instance_type));
Label non_callable, class_constructor;
__ JumpIfSmi(x1, &non_callable);
__ LoadMap(x4, x1);
__ CompareInstanceTypeRange(x4, x5, 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, ls);
__ Cmp(x5, JS_BOUND_FUNCTION_TYPE);
__ Cmp(instance_type, JS_BOUND_FUNCTION_TYPE);
__ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Check if target has a [[Call]] internal method.
__ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x4, Map::Bits1::IsCallableBit::kMask,
&non_callable);
{
Register flags = x4;
__ Ldrb(flags, FieldMemOperand(map, Map::kBitFieldOffset));
map = no_reg;
__ TestAndBranchIfAllClear(flags, Map::Bits1::IsCallableBit::kMask,
&non_callable);
}
// Check if target is a proxy and call CallProxy external builtin
__ Cmp(x5, JS_PROXY_TYPE);
__ Cmp(instance_type, 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".
__ Cmp(x5, JS_CLASS_CONSTRUCTOR_TYPE);
__ Cmp(instance_type, JS_CLASS_CONSTRUCTOR_TYPE);
__ B(eq, &class_constructor);
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
// Overwrite the original receiver with the (original) target.
__ Poke(x1, __ ReceiverOperand(x0));
__ Poke(target, __ ReceiverOperand(argc));
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(x1, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2932,7 +2942,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ PushArgument(x1);
__ PushArgument(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
__ Unreachable();
}
......@@ -2941,7 +2951,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameScope frame(masm, StackFrame::INTERNAL);
__ PushArgument(x1);
__ PushArgument(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Unreachable();
}
......@@ -3015,31 +3025,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// -- x3 : the new target (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -----------------------------------
Register argc = x0;
Register target = x1;
Register map = x4;
Register instance_type = x5;
DCHECK(!AreAliased(argc, target, map, instance_type));
// Check if target is a Smi.
Label non_constructor, non_proxy;
__ JumpIfSmi(x1, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ LoadTaggedPointerField(x4, FieldMemOperand(x1, HeapObject::kMapOffset));
__ Ldrb(x2, FieldMemOperand(x4, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x2, Map::Bits1::IsConstructorBit::kMask,
&non_constructor);
__ LoadTaggedPointerField(map,
FieldMemOperand(target, HeapObject::kMapOffset));
{
Register flags = x2;
DCHECK(!AreAliased(argc, target, map, instance_type, flags));
__ Ldrb(flags, FieldMemOperand(map, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(flags, Map::Bits1::IsConstructorBit::kMask,
&non_constructor);
}
// Dispatch based on instance type.
__ CompareInstanceTypeRange(x4, x5, 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, ls);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ Cmp(x5, JS_BOUND_FUNCTION_TYPE);
__ Cmp(instance_type, JS_BOUND_FUNCTION_TYPE);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction),
RelocInfo::CODE_TARGET, eq);
// Only dispatch to proxies after checking whether they are constructors.
__ Cmp(x5, JS_PROXY_TYPE);
__ Cmp(instance_type, JS_PROXY_TYPE);
__ B(ne, &non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy),
RelocInfo::CODE_TARGET);
......@@ -3048,10 +3068,11 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ bind(&non_proxy);
{
// Overwrite the original receiver with the (original) target.
__ Poke(x1, __ ReceiverOperand(x0));
__ Poke(target, __ ReceiverOperand(argc));
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(x1, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -2642,49 +2642,56 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- eax : the number of arguments
// -- edi : the target to call (can be any Object).
// -----------------------------------
StackArgumentsAccessor args(eax);
Register argc = eax;
Register target = edi;
Register map = ecx;
Register instance_type = edx;
DCHECK(!AreAliased(argc, target, map, instance_type));
StackArgumentsAccessor args(argc);
Label non_callable, non_smi, non_callable_jsfunction, non_jsboundfunction,
non_proxy, class_constructor;
__ JumpIfSmi(edi, &non_callable);
__ JumpIfSmi(target, &non_callable);
__ bind(&non_smi);
__ LoadMap(ecx, edi);
__ CmpInstanceTypeRange(ecx, ecx, FIRST_CALLABLE_JS_FUNCTION_TYPE,
__ LoadMap(map, target);
__ CmpInstanceTypeRange(map, instance_type, map,
FIRST_CALLABLE_JS_FUNCTION_TYPE,
LAST_CALLABLE_JS_FUNCTION_TYPE);
__ j(above, &non_callable_jsfunction);
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
RelocInfo::CODE_TARGET);
__ bind(&non_callable_jsfunction);
__ LoadMap(ecx, edi);
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
__ cmpw(instance_type, Immediate(JS_BOUND_FUNCTION_TYPE));
__ j(not_equal, &non_jsboundfunction);
__ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction),
RelocInfo::CODE_TARGET);
// Check if target is a proxy and call CallProxy external builtin
__ bind(&non_jsboundfunction);
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
__ LoadMap(map, target);
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
Immediate(Map::Bits1::IsCallableBit::kMask));
__ j(zero, &non_callable);
// Call CallProxy external builtin
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
__ cmpw(instance_type, Immediate(JS_PROXY_TYPE));
__ j(not_equal, &non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET);
// ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
// Check that the function is not a "classConstructor".
__ bind(&non_proxy);
__ CmpInstanceType(ecx, JS_CLASS_CONSTRUCTOR_TYPE);
__ cmpw(instance_type, Immediate(JS_CLASS_CONSTRUCTOR_TYPE));
__ j(equal, &class_constructor);
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
// Overwrite the original receiver with the (original) target.
__ mov(args.GetReceiverOperand(), edi);
__ mov(args.GetReceiverOperand(), target);
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(edi, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2693,7 +2700,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(edi);
__ Push(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
__ Trap(); // Unreachable.
}
......@@ -2702,7 +2709,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameScope frame(masm, StackFrame::INTERNAL);
__ Push(edi);
__ Push(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Trap(); // Unreachable.
}
......@@ -2775,20 +2782,25 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially)
// -- edi : the constructor to call (can be any Object)
// -----------------------------------
StackArgumentsAccessor args(eax);
Register argc = eax;
Register target = edi;
Register map = ecx;
DCHECK(!AreAliased(argc, target, map));
StackArgumentsAccessor args(argc);
// Check if target is a Smi.
Label non_constructor, non_proxy, non_jsfunction, non_jsboundfunction;
__ JumpIfSmi(edi, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
__ mov(map, FieldOperand(target, HeapObject::kMapOffset));
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
Immediate(Map::Bits1::IsConstructorBit::kMask));
__ j(zero, &non_constructor);
// Dispatch based on instance type.
__ CmpInstanceTypeRange(ecx, ecx, FIRST_JS_FUNCTION_TYPE,
__ CmpInstanceTypeRange(map, map, map, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
__ j(above, &non_jsfunction);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction),
......@@ -2797,15 +2809,15 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// Only dispatch to bound functions after checking whether they are
// constructors.
__ bind(&non_jsfunction);
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
__ mov(map, FieldOperand(target, HeapObject::kMapOffset));
__ CmpInstanceType(map, JS_BOUND_FUNCTION_TYPE);
__ j(not_equal, &non_jsboundfunction);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction),
RelocInfo::CODE_TARGET);
// Only dispatch to proxies after checking whether they are constructors.
__ bind(&non_jsboundfunction);
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
__ CmpInstanceType(map, JS_PROXY_TYPE);
__ j(not_equal, &non_proxy);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy),
RelocInfo::CODE_TARGET);
......@@ -2814,9 +2826,10 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ bind(&non_proxy);
{
// Overwrite the original receiver with the (original) target.
__ mov(args.GetReceiverOperand(), edi);
__ mov(args.GetReceiverOperand(), target);
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(edi, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -2574,42 +2574,48 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// -- rax : the number of arguments
// -- rdi : the target to call (can be any Object)
// -----------------------------------
StackArgumentsAccessor args(rax);
Register argc = rax;
Register target = rdi;
Register map = rcx;
Register instance_type = rdx;
DCHECK(!AreAliased(argc, target, map, instance_type));
StackArgumentsAccessor args(argc);
Label non_callable, class_constructor;
__ JumpIfSmi(rdi, &non_callable);
__ LoadMap(rcx, rdi);
__ CmpInstanceTypeRange(rcx, FIRST_CALLABLE_JS_FUNCTION_TYPE,
__ JumpIfSmi(target, &non_callable);
__ LoadMap(map, target);
__ CmpInstanceTypeRange(map, instance_type, FIRST_CALLABLE_JS_FUNCTION_TYPE,
LAST_CALLABLE_JS_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
RelocInfo::CODE_TARGET, below_equal);
__ CmpInstanceType(rcx, JS_BOUND_FUNCTION_TYPE);
__ cmpw(instance_type, Immediate(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction),
RelocInfo::CODE_TARGET, equal);
// Check if target has a [[Call]] internal method.
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
__ testb(FieldOperand(map, Map::kBitFieldOffset),
Immediate(Map::Bits1::IsCallableBit::kMask));
__ j(zero, &non_callable, Label::kNear);
// Check if target is a proxy and call CallProxy external builtin
__ CmpInstanceType(rcx, JS_PROXY_TYPE);
__ cmpw(instance_type, Immediate(JS_PROXY_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET,
equal);
// ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
// Check that the function is not a "classConstructor".
__ CmpInstanceType(rcx, JS_CLASS_CONSTRUCTOR_TYPE);
__ cmpw(instance_type, Immediate(JS_CLASS_CONSTRUCTOR_TYPE));
__ j(equal, &class_constructor);
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
// Overwrite the original receiver with the (original) target.
__ movq(args.GetReceiverOperand(), rdi);
__ movq(args.GetReceiverOperand(), target);
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(rdi, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(
ConvertReceiverMode::kNotNullOrUndefined),
RelocInfo::CODE_TARGET);
......@@ -2618,7 +2624,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&non_callable);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rdi);
__ Push(target);
__ CallRuntime(Runtime::kThrowCalledNonCallable);
__ Trap(); // Unreachable.
}
......@@ -2627,7 +2633,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ bind(&class_constructor);
{
FrameScope frame(masm, StackFrame::INTERNAL);
__ Push(rdi);
__ Push(target);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
__ Trap(); // Unreachable.
}
......@@ -2696,40 +2702,48 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially)
// -- rdi : the constructor to call (can be any Object)
// -----------------------------------
StackArgumentsAccessor args(rax);
Register argc = rax;
Register target = rdi;
Register map = rcx;
Register instance_type = r8;
DCHECK(!AreAliased(argc, target, map, instance_type));
StackArgumentsAccessor args(argc);
// Check if target is a Smi.
Label non_constructor;
__ JumpIfSmi(rdi, &non_constructor);
__ JumpIfSmi(target, &non_constructor);
// Check if target has a [[Construct]] internal method.
__ LoadMap(rcx, rdi);
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
__ LoadMap(map, target);
__ testb(FieldOperand(map, Map::kBitFieldOffset),
Immediate(Map::Bits1::IsConstructorBit::kMask));
__ j(zero, &non_constructor);
// Dispatch based on instance type.
__ CmpInstanceTypeRange(rcx, FIRST_JS_FUNCTION_TYPE, LAST_JS_FUNCTION_TYPE);
__ CmpInstanceTypeRange(map, instance_type, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction),
RelocInfo::CODE_TARGET, below_equal);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ CmpInstanceType(rcx, JS_BOUND_FUNCTION_TYPE);
__ cmpw(instance_type, Immediate(JS_BOUND_FUNCTION_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction),
RelocInfo::CODE_TARGET, equal);
// Only dispatch to proxies after checking whether they are constructors.
__ CmpInstanceType(rcx, JS_PROXY_TYPE);
__ cmpw(instance_type, Immediate(JS_PROXY_TYPE));
__ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy), RelocInfo::CODE_TARGET,
equal);
// Called Construct on an exotic Object with a [[Construct]] internal method.
{
// Overwrite the original receiver with the (original) target.
__ movq(args.GetReceiverOperand(), rdi);
__ movq(args.GetReceiverOperand(), target);
// Let the "call_as_constructor_delegate" take care of the rest.
__ LoadNativeContextSlot(rdi, Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ LoadNativeContextSlot(target,
Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX);
__ Jump(masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET);
}
......
......@@ -1877,16 +1877,26 @@ void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
cmp(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();
sub(scratch, value, Operand(lower_limit));
cmp(scratch, Operand(higher_limit - lower_limit));
} else {
cmp(value, Operand(higher_limit));
}
}
void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
InstanceType lower_limit,
InstanceType higher_limit) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
sub(scratch, type_reg, Operand(lower_limit));
cmp(scratch, Operand(higher_limit - lower_limit));
CompareRange(type_reg, lower_limit, higher_limit);
}
void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
......@@ -1901,14 +1911,7 @@ void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit,
Label* on_in_range) {
ASM_CODE_COMMENT(this);
if (lower_limit != 0) {
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
sub(scratch, value, Operand(lower_limit));
cmp(scratch, Operand(higher_limit - lower_limit));
} else {
cmp(value, Operand(higher_limit));
}
CompareRange(value, lower_limit, higher_limit);
b(ls, on_in_range);
}
......
......@@ -757,7 +757,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
}
// Checks if value is in range [lower_limit, higher_limit] using a single
// comparison.
// comparison. Flags C=0 or Z=1 indicate the value is in the range (condition
// ls).
void CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit);
void JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range);
......
......@@ -158,16 +158,23 @@ void MacroAssembler::PushRoot(RootIndex index) {
}
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Register scratch,
Label* on_in_range,
Label::Distance near_jump) {
void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit, Register scratch) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
if (lower_limit != 0) {
lea(scratch, Operand(value, 0u - lower_limit));
cmp(scratch, Immediate(higher_limit - lower_limit));
} else {
cmp(value, Immediate(higher_limit));
}
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Register scratch,
Label* on_in_range,
Label::Distance near_jump) {
CompareRange(value, lower_limit, higher_limit, scratch);
j(below_equal, on_in_range, near_jump);
}
......@@ -723,14 +730,15 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
cmpw(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type));
}
void MacroAssembler::CmpInstanceTypeRange(Register map, Register scratch,
void MacroAssembler::CmpInstanceTypeRange(Register map,
Register instance_type_out,
Register scratch,
InstanceType lower_limit,
InstanceType higher_limit) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
movzx_w(scratch, FieldOperand(map, Map::kInstanceTypeOffset));
lea(scratch, Operand(scratch, 0u - lower_limit));
cmp(scratch, Immediate(higher_limit - lower_limit));
movzx_w(instance_type_out, FieldOperand(map, Map::kInstanceTypeOffset));
CompareRange(instance_type_out, lower_limit, higher_limit, scratch);
}
void MacroAssembler::AssertSmi(Register object) {
......@@ -762,7 +770,7 @@ void MacroAssembler::AssertFunction(Register object, Register scratch) {
Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
Push(object);
LoadMap(object, object);
CmpInstanceTypeRange(object, scratch, FIRST_JS_FUNCTION_TYPE,
CmpInstanceTypeRange(object, scratch, scratch, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
Pop(object);
Check(below_equal, AbortReason::kOperandIsNotAFunction);
......
......@@ -435,7 +435,11 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
}
// Checks if value is in range [lower_limit, higher_limit] using a single
// comparison.
// comparison. Flags CF=1 or ZF=1 indicate the value is in the range
// (condition below_equal). It is valid, that |value| == |scratch| as far as
// this function is concerned.
void CompareRange(Register value, unsigned lower_limit, unsigned higher_limit,
Register scratch);
void JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Register scratch,
Label* on_in_range,
......@@ -519,8 +523,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
//
// Always use unsigned comparisons: below_equal for a positive
// result.
void CmpInstanceTypeRange(Register map, Register scratch,
InstanceType lower_limit,
void CmpInstanceTypeRange(Register map, Register instance_type_out,
Register scratch, InstanceType lower_limit,
InstanceType higher_limit);
// Smi tagging support.
......
......@@ -1676,15 +1676,22 @@ void MacroAssembler::Cmp(Operand dst, Handle<Object> source) {
}
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range,
Label::Distance near_jump) {
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) {
leal(kScratchRegister, Operand(value, 0u - lower_limit));
cmpl(kScratchRegister, Immediate(higher_limit - lower_limit));
} else {
cmpl(value, Immediate(higher_limit));
}
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range,
Label::Distance near_jump) {
CompareRange(value, lower_limit, higher_limit);
j(below_equal, on_in_range, near_jump);
}
......@@ -2331,12 +2338,12 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
}
void MacroAssembler::CmpInstanceTypeRange(Register map,
Register instance_type_out,
InstanceType lower_limit,
InstanceType higher_limit) {
DCHECK_LT(lower_limit, higher_limit);
movzxwl(kScratchRegister, FieldOperand(map, Map::kInstanceTypeOffset));
leal(kScratchRegister, Operand(kScratchRegister, 0u - lower_limit));
cmpl(kScratchRegister, Immediate(higher_limit - lower_limit));
movzxwl(instance_type_out, FieldOperand(map, Map::kInstanceTypeOffset));
CompareRange(instance_type_out, lower_limit, higher_limit);
}
void TurboAssembler::AssertNotSmi(Register object) {
......@@ -2401,7 +2408,8 @@ void MacroAssembler::AssertFunction(Register object) {
Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
Push(object);
LoadMap(object, object);
CmpInstanceTypeRange(object, FIRST_JS_FUNCTION_TYPE, LAST_JS_FUNCTION_TYPE);
CmpInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
Pop(object);
Check(below_equal, AbortReason::kOperandIsNotAFunction);
}
......
......@@ -750,7 +750,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
void Cmp(Operand dst, Handle<Object> source);
// Checks if value is in range [lower_limit, higher_limit] using a single
// comparison.
// comparison. Flags CF=1 or ZF=1 indicate the value is in the range
// (condition below_equal).
void CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit);
void JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range,
Label::Distance near_jump = Label::kFar);
......@@ -784,7 +787,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Compare instance type ranges for a map (low and high inclusive)
// Always use unsigned comparisons: below_equal for a positive result.
void CmpInstanceTypeRange(Register map, InstanceType low, InstanceType high);
void CmpInstanceTypeRange(Register map, Register instance_type_out,
InstanceType low, InstanceType high);
template <typename Field>
void DecodeField(Register reg) {
......
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