Eliminate special keyed load string stub in favor of uniform handlers.

KeyedLoadIC installs a special case if the receiver is a string.
Although there are several maps for strings, in practice we seem to
be able to treat them individually because a given KeyedLoad site
only sees 1-2 string types.

R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/602773003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24661 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mvstanton@chromium.org 2014-10-16 11:42:47 +00:00
parent fe3a8845f0
commit ecac8b0877
19 changed files with 218 additions and 200 deletions

View File

@ -1503,6 +1503,34 @@ void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
}
void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
// Return address is in lr.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = r3;
Register result = r0;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX,
RECEIVER_IS_STRING);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
PropertyAccessCompiler::TailCallBuiltin(
masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC));
}
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// The displacement is the offset of the last parameter (if any)
// relative to the frame pointer.

View File

@ -1422,6 +1422,34 @@ void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
}
void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
// Return address is in lr.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register result = x0;
Register scratch = x3;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX,
RECEIVER_IS_STRING);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ Bind(&miss);
PropertyAccessCompiler::TailCallBuiltin(
masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC));
}
void InstanceofStub::Generate(MacroAssembler* masm) {
// Stack on entry:
// jssp[0]: function.

View File

@ -1279,11 +1279,6 @@ static void Generate_KeyedLoadIC_Generic(MacroAssembler* masm) {
}
static void Generate_KeyedLoadIC_String(MacroAssembler* masm) {
KeyedLoadIC::GenerateString(masm);
}
static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
KeyedLoadIC::GeneratePreMonomorphic(masm);
}

View File

@ -88,7 +88,6 @@ enum BuiltinExtraArguments {
V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC, \
kNoExtraICState) \
V(KeyedLoadIC_Generic, KEYED_LOAD_IC, GENERIC, kNoExtraICState) \
V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, kNoExtraICState) \
\
V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, StoreIC::kStrictModeState) \
\

View File

@ -39,6 +39,7 @@ namespace internal {
V(KeyedLoadICTrampoline) \
V(LoadICTrampoline) \
V(LoadIndexedInterceptor) \
V(LoadIndexedString) \
V(MathPow) \
V(ProfileEntryHook) \
V(RecordWrite) \
@ -882,6 +883,19 @@ class LoadIndexedInterceptorStub : public PlatformCodeStub {
};
class LoadIndexedStringStub : public PlatformCodeStub {
public:
explicit LoadIndexedStringStub(Isolate* isolate)
: PlatformCodeStub(isolate) {}
virtual Code::Kind GetCodeKind() const { return Code::HANDLER; }
virtual Code::StubType GetStubType() { return Code::FAST; }
DEFINE_CALL_INTERFACE_DESCRIPTOR(Load);
DEFINE_PLATFORM_CODE_STUB(LoadIndexedString, PlatformCodeStub);
};
class HandlerStub : public HydrogenCodeStub {
public:
virtual Code::Kind GetCodeKind() const { return Code::HANDLER; }
@ -1657,6 +1671,15 @@ enum StringIndexFlags {
};
enum ReceiverCheckMode {
// We don't know anything about the receiver.
RECEIVER_IS_UNKNOWN,
// We know the receiver is a string.
RECEIVER_IS_STRING
};
// Generates code implementing String.prototype.charCodeAt.
//
// Only supports the case when the receiver is a string and the index
@ -1669,20 +1692,19 @@ enum StringIndexFlags {
// preserved, |scratch| and |result| are clobbered.
class StringCharCodeAtGenerator {
public:
StringCharCodeAtGenerator(Register object,
Register index,
Register result,
Label* receiver_not_string,
Label* index_not_number,
StringCharCodeAtGenerator(Register object, Register index, Register result,
Label* receiver_not_string, Label* index_not_number,
Label* index_out_of_range,
StringIndexFlags index_flags)
StringIndexFlags index_flags,
ReceiverCheckMode check_mode = RECEIVER_IS_UNKNOWN)
: object_(object),
index_(index),
result_(result),
receiver_not_string_(receiver_not_string),
index_not_number_(index_not_number),
index_out_of_range_(index_out_of_range),
index_flags_(index_flags) {
index_flags_(index_flags),
check_mode_(check_mode) {
DCHECK(!result_.is(object_));
DCHECK(!result_.is(index_));
}
@ -1714,6 +1736,7 @@ class StringCharCodeAtGenerator {
Label* index_out_of_range_;
StringIndexFlags index_flags_;
ReceiverCheckMode check_mode_;
Label call_runtime_;
Label index_not_smi_;
@ -1773,20 +1796,13 @@ class StringCharFromCodeGenerator {
// preserved, |scratch1|, |scratch2|, and |result| are clobbered.
class StringCharAtGenerator {
public:
StringCharAtGenerator(Register object,
Register index,
Register scratch,
Register result,
Label* receiver_not_string,
Label* index_not_number,
Label* index_out_of_range,
StringIndexFlags index_flags)
: char_code_at_generator_(object,
index,
scratch,
receiver_not_string,
index_not_number,
index_out_of_range,
StringCharAtGenerator(Register object, Register index, Register scratch,
Register result, Label* receiver_not_string,
Label* index_not_number, Label* index_out_of_range,
StringIndexFlags index_flags,
ReceiverCheckMode check_mode = RECEIVER_IS_UNKNOWN)
: char_code_at_generator_(object, index, scratch, receiver_not_string,
index_not_number, index_out_of_range,
index_flags),
char_from_code_generator_(scratch, result) {}

View File

@ -691,6 +691,37 @@ void LoadIndexedInterceptorStub::Generate(MacroAssembler* masm) {
}
void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
// Return address is on the stack.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = ebx;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
Register result = eax;
DCHECK(!result.is(scratch));
// TODO(mvstanton): the generator doesn't need to verify that
// receiver is a string map, that is done outside the handler.
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX,
RECEIVER_IS_STRING);
char_at_generator.GenerateFast(masm);
__ ret(0);
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
PropertyAccessCompiler::TailCallBuiltin(
masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC));
}
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// The key is in edx and the parameter count is in eax.
DCHECK(edx.is(ArgumentsAccessReadDescriptor::index()));
@ -2747,14 +2778,16 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
// If the receiver is a smi trigger the non-string case.
STATIC_ASSERT(kSmiTag == 0);
__ JumpIfSmi(object_, receiver_not_string_);
if (check_mode_ == RECEIVER_IS_UNKNOWN) {
__ JumpIfSmi(object_, receiver_not_string_);
// Fetch the instance type of the receiver into result register.
__ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
__ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
// If the receiver is not a string trigger the non-string case.
__ test(result_, Immediate(kIsNotStringMask));
__ j(not_zero, receiver_not_string_);
// Fetch the instance type of the receiver into result register.
__ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
__ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
// If the receiver is not a string trigger the non-string case.
__ test(result_, Immediate(kIsNotStringMask));
__ j(not_zero, receiver_not_string_);
}
// If the index is non-smi trigger the non-smi case.
STATIC_ASSERT(kSmiTag == 0);

View File

@ -587,32 +587,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// Return address is in lr.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = r3;
Register result = r0;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
GenerateMiss(masm);
}
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// Push receiver, key and value for runtime call.
__ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),

View File

@ -627,32 +627,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// Return address is in lr.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register result = x0;
Register scratch = x3;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ Bind(&miss);
GenerateMiss(masm);
}
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
ASM_LOCATION("KeyedStoreIC::GenerateMiss");

View File

@ -415,8 +415,8 @@ void ElementHandlerCompiler::CompileElementHandlers(
Handle<Map> receiver_map = receiver_maps->at(i);
Handle<Code> cached_stub;
if ((receiver_map->instance_type() & kNotStringTag) == 0) {
cached_stub = isolate()->builtins()->KeyedLoadIC_String();
if (receiver_map->IsStringMap()) {
cached_stub = LoadIndexedStringStub(isolate()).GetCode();
} else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
cached_stub = isolate()->builtins()->KeyedLoadIC_Slow();
} else {

View File

@ -476,33 +476,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// Return address is on the stack.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = ebx;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
Register result = eax;
DCHECK(!result.is(scratch));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ ret(0);
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
GenerateMiss(masm);
}
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
// Return address is on the stack.
Label slow, notin;

View File

@ -96,6 +96,9 @@ Handle<Code> PropertyICCompiler::ComputeKeyedLoadMonomorphic(
Handle<Code> stub;
if (receiver_map->has_indexed_interceptor()) {
stub = LoadIndexedInterceptorStub(isolate).GetCode();
} else if (receiver_map->IsStringMap()) {
// We have a string.
stub = LoadIndexedStringStub(isolate).GetCode();
} else if (receiver_map->has_sloppy_arguments_elements()) {
stub = KeyedLoadSloppyArgumentsStub(isolate).GetCode();
} else if (receiver_map->has_fast_elements() ||

View File

@ -512,15 +512,10 @@ void KeyedLoadIC::Clear(Isolate* isolate, Address address, Code* target,
ConstantPoolArray* constant_pool) {
if (IsCleared(target)) return;
// If the target is the string_stub, then don't clear it. It is the
// perfect stub if we continue to see strings. Holding this
// state is not preventing learning new information.
if (target != *isolate->builtins()->KeyedLoadIC_String()) {
// Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive
// through the embedded maps.
SetTargetAtAddress(address, *pre_monomorphic_stub(isolate), constant_pool);
}
// Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive
// through the embedded maps.
SetTargetAtAddress(address, *pre_monomorphic_stub(isolate), constant_pool);
}
@ -1162,14 +1157,11 @@ static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
}
Handle<Code> KeyedLoadIC::LoadElementStub(Handle<JSObject> receiver) {
Handle<Code> KeyedLoadIC::LoadElementStub(Handle<HeapObject> receiver) {
Handle<Map> receiver_map(receiver->map(), isolate());
MapHandleList target_receiver_maps;
if (target().is_identical_to(string_stub())) {
target_receiver_maps.Add(isolate()->factory()->string_map());
} else {
TargetMaps(&target_receiver_maps);
}
TargetMaps(&target_receiver_maps);
if (target_receiver_maps.length() == 0) {
return PropertyICCompiler::ComputeKeyedLoadMonomorphic(receiver_map);
}
@ -1181,9 +1173,10 @@ Handle<Code> KeyedLoadIC::LoadElementStub(Handle<JSObject> receiver) {
// monomorphic. If this optimistic assumption is not true, the IC will
// miss again and it will become polymorphic and support both the
// untransitioned and transitioned maps.
if (state() == MONOMORPHIC && IsMoreGeneralElementsKindTransition(
target_receiver_maps.at(0)->elements_kind(),
receiver->GetElementsKind())) {
if (state() == MONOMORPHIC && !receiver->IsString() &&
IsMoreGeneralElementsKindTransition(
target_receiver_maps.at(0)->elements_kind(),
Handle<JSObject>::cast(receiver)->GetElementsKind())) {
return PropertyICCompiler::ComputeKeyedLoadMonomorphic(receiver_map);
}
@ -1231,11 +1224,9 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
LoadIC::Load(object, Handle<Name>::cast(key)),
Object);
} else if (FLAG_use_ic && !object->IsAccessCheckNeeded()) {
if (object->IsString() && key->IsNumber()) {
if (state() == UNINITIALIZED) stub = string_stub();
} else if (object->IsJSObject()) {
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (!Object::ToSmi(isolate(), key).is_null()) {
if (object->IsJSObject() || (object->IsString() && key->IsNumber())) {
Handle<HeapObject> receiver = Handle<HeapObject>::cast(object);
if (object->IsString() || !Object::ToSmi(isolate(), key).is_null()) {
stub = LoadElementStub(receiver);
}
}

View File

@ -413,7 +413,6 @@ class KeyedLoadIC : public LoadIC {
GenerateMiss(masm);
}
static void GenerateGeneric(MacroAssembler* masm);
static void GenerateString(MacroAssembler* masm);
// Bit mask to be tested against bit field for the cases when
// generic stub should go into slow case.
@ -428,16 +427,14 @@ class KeyedLoadIC : public LoadIC {
static Handle<Code> pre_monomorphic_stub(Isolate* isolate);
protected:
Handle<Code> LoadElementStub(Handle<JSObject> receiver);
// receiver is HeapObject because it could be a String or a JSObject
Handle<Code> LoadElementStub(Handle<HeapObject> receiver);
virtual Handle<Code> pre_monomorphic_stub() const {
return pre_monomorphic_stub(isolate());
}
private:
Handle<Code> generic_stub() const { return generic_stub(isolate()); }
Handle<Code> string_stub() {
return isolate()->builtins()->KeyedLoadIC_String();
}
static void Clear(Isolate* isolate, Address address, Code* target,
ConstantPoolArray* constant_pool);

View File

@ -594,32 +594,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// Return address is in ra.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = a3;
Register result = v0;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
GenerateMiss(masm);
}
static void KeyedStoreGenerateGenericHelper(
MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length,

View File

@ -403,32 +403,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// Return address is on the stack.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = rbx;
Register result = rax;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ ret(0);
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
GenerateMiss(masm);
}
static void KeyedStoreGenerateGenericHelper(
MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length) {

View File

@ -1403,6 +1403,34 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
}
void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
// Return address is in ra.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = a3;
Register result = v0;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX,
RECEIVER_IS_STRING);
char_at_generator.GenerateFast(masm);
__ Ret();
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
PropertyAccessCompiler::TailCallBuiltin(
masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC));
}
// Uses registers a0 to t0.
// Expected input (depending on whether args are in registers or on the stack):
// * object: a0 or at sp + 1 * kPointerSize.

View File

@ -6140,6 +6140,7 @@ class Map: public HeapObject {
bool IsJSObjectMap() {
return instance_type() >= FIRST_JS_OBJECT_TYPE;
}
bool IsStringMap() { return instance_type() < FIRST_NONSTRING_TYPE; }
bool IsJSProxyMap() {
InstanceType type = instance_type();
return FIRST_JS_PROXY_TYPE <= type && type <= LAST_JS_PROXY_TYPE;

View File

@ -260,12 +260,14 @@ void TypeFeedbackOracle::PropertyReceiverTypes(TypeFeedbackId id,
void TypeFeedbackOracle::KeyedPropertyReceiverTypes(
TypeFeedbackId id, SmallMapList* receiver_types, bool* is_string) {
receiver_types->Clear();
*is_string = false;
if (LoadIsBuiltin(id, Builtins::kKeyedLoadIC_String)) {
*is_string = true;
} else {
CollectReceiverTypes(id, receiver_types);
CollectReceiverTypes(id, receiver_types);
// Are all the receiver maps string maps?
bool all_strings = receiver_types->length() > 0;
for (int i = 0; i < receiver_types->length(); i++) {
all_strings &= receiver_types->at(i)->IsStringMap();
}
*is_string = all_strings;
}

View File

@ -867,6 +867,34 @@ void LoadIndexedInterceptorStub::Generate(MacroAssembler* masm) {
}
void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
// Return address is on the stack.
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();
Register index = LoadDescriptor::NameRegister();
Register scratch = rbx;
Register result = rax;
DCHECK(!scratch.is(receiver) && !scratch.is(index));
StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
&miss, // When not a string.
&miss, // When not a number.
&miss, // When index out of range.
STRING_INDEX_IS_ARRAY_INDEX,
RECEIVER_IS_STRING);
char_at_generator.GenerateFast(masm);
__ ret(0);
StubRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
PropertyAccessCompiler::TailCallBuiltin(
masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC));
}
void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
// rsp[0] : return address
// rsp[8] : number of parameters