diff --git a/src/arm64/assembler-arm64.h b/src/arm64/assembler-arm64.h index df8a731d9d..0f77e46e1d 100644 --- a/src/arm64/assembler-arm64.h +++ b/src/arm64/assembler-arm64.h @@ -84,7 +84,7 @@ const int kNumSafepointRegisters = 32; // space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. #define kSafepointSavedRegisters CPURegList::GetSafepointSavedRegisters().list() #define kNumSafepointSavedRegisters \ - CPURegList::GetSafepointSavedRegisters().Count(); + CPURegList::GetSafepointSavedRegisters().Count() // Some CPURegister methods can return Register and VRegister types, so we // need to declare them in advance. @@ -456,6 +456,9 @@ ALIAS_REGISTER(Register, lr, x30); ALIAS_REGISTER(Register, xzr, x31); ALIAS_REGISTER(Register, wzr, w31); +// Register used for padding stack slots. +ALIAS_REGISTER(Register, padreg, x31); + // Keeps the 0 double value. ALIAS_REGISTER(VRegister, fp_zero, d15); // MacroAssembler fixed V Registers. diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index d9197569dd..a67370dd96 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -33,8 +33,7 @@ namespace internal { void ArrayNArgumentsConstructorStub::Generate(MacroAssembler* masm) { __ Mov(x5, Operand(x0, LSL, kPointerSizeLog2)); __ Str(x1, MemOperand(jssp, x5)); - __ Push(x1); - __ Push(x2); + __ Push(x1, x2); __ Add(x0, x0, Operand(3)); __ TailCallRuntime(Runtime::kNewArray); } @@ -620,25 +619,26 @@ void JSEntryStub::Generate(MacroAssembler* masm) { ExternalReference js_entry_sp(IsolateAddressId::kJSEntrySPAddress, isolate()); __ Mov(x10, ExternalReference(js_entry_sp)); __ Ldr(x11, MemOperand(x10)); - __ Cbnz(x11, &non_outermost_js); - __ Str(fp, MemOperand(x10)); - __ Mov(x12, StackFrame::OUTERMOST_JSENTRY_FRAME); - __ Push(x12); - __ B(&done); - __ Bind(&non_outermost_js); - // We spare one instruction by pushing xzr since the marker is 0. + + // Select between the inner and outermost frame marker, based on the JS entry + // sp. We assert that the inner marker is zero, so we can use xzr to save a + // move instruction. DCHECK(StackFrame::INNER_JSENTRY_FRAME == 0); - __ Push(xzr); + __ Cmp(x11, 0); // If x11 is zero, this is the outermost frame. + __ Csel(x12, xzr, StackFrame::OUTERMOST_JSENTRY_FRAME, ne); + __ B(ne, &done); + __ Str(fp, MemOperand(x10)); + __ Bind(&done); + __ Push(x12); // The frame set up looks like this: // jssp[0] : JS entry frame marker. // jssp[1] : C entry FP. // jssp[2] : stack frame marker. - // jssp[3] : stack frmae marker. + // jssp[3] : stack frame marker. // jssp[4] : bad frame pointer 0xfff...ff <- fp points here. - // Jump to a faked try block that does the invoke, with a faked catch // block that sets the pending exception. __ B(&invoke); @@ -665,7 +665,22 @@ void JSEntryStub::Generate(MacroAssembler* masm) { // Invoke: Link this frame into the handler chain. __ Bind(&invoke); - __ PushStackHandler(); + + // Push new stack handler. + DCHECK(jssp.Is(__ StackPointer())); + static_assert(StackHandlerConstants::kSize == 1 * kPointerSize, + "Unexpected offset for StackHandlerConstants::kSize"); + static_assert(StackHandlerConstants::kNextOffset == 0 * kPointerSize, + "Unexpected offset for StackHandlerConstants::kNextOffset"); + + // Link the current handler as the next handler. + __ Mov(x11, ExternalReference(IsolateAddressId::kHandlerAddress, isolate())); + __ Ldr(x10, MemOperand(x11)); + __ Push(x10); + + // Set this new handler as the current one. + __ Str(jssp, MemOperand(x11)); + // If an exception not caught by another handler occurs, this handler // returns control to the code after the B(&invoke) above, which // restores all callee-saved registers (including cp and fp) to their @@ -689,9 +704,13 @@ void JSEntryStub::Generate(MacroAssembler* masm) { __ Call(BUILTIN_CODE(isolate(), JSEntryTrampoline), RelocInfo::CODE_TARGET); } - // Unlink this frame from the handler chain. - __ PopStackHandler(); - + // Pop the stack handler and unlink this frame from the handler chain. + static_assert(StackHandlerConstants::kNextOffset == 0 * kPointerSize, + "Unexpected offset for StackHandlerConstants::kNextOffset"); + __ Pop(x10); + __ Mov(x11, ExternalReference(IsolateAddressId::kHandlerAddress, isolate())); + __ Drop(StackHandlerConstants::kSize - kXRegSize, kByteSizeInBytes); + __ Str(x10, MemOperand(x11)); __ Bind(&exit); // x0 holds the result. @@ -705,17 +724,20 @@ void JSEntryStub::Generate(MacroAssembler* masm) { // Check if the current stack frame is marked as the outermost JS frame. Label non_outermost_js_2; - __ Pop(x10); - __ Cmp(x10, StackFrame::OUTERMOST_JSENTRY_FRAME); - __ B(ne, &non_outermost_js_2); - __ Mov(x11, ExternalReference(js_entry_sp)); - __ Str(xzr, MemOperand(x11)); - __ Bind(&non_outermost_js_2); + { + Register c_entry_fp = x11; + __ Pop(x10, c_entry_fp); + __ Cmp(x10, StackFrame::OUTERMOST_JSENTRY_FRAME); + __ B(ne, &non_outermost_js_2); + __ Mov(x12, ExternalReference(js_entry_sp)); + __ Str(xzr, MemOperand(x12)); + __ Bind(&non_outermost_js_2); - // Restore the top frame descriptors from the stack. - __ Pop(x10); - __ Mov(x11, ExternalReference(IsolateAddressId::kCEntryFPAddress, isolate())); - __ Str(x10, MemOperand(x11)); + // Restore the top frame descriptors from the stack. + __ Mov(x12, + ExternalReference(IsolateAddressId::kCEntryFPAddress, isolate())); + __ Str(c_entry_fp, MemOperand(x12)); + } // Reset the stack to the callee saved registers. __ Drop(-EntryFrameConstants::kCallerFPOffset, kByteSizeInBytes); @@ -1195,8 +1217,10 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, } CPURegList spill_list(CPURegister::kRegister, kXRegSizeInBits, 0, 6); - spill_list.Combine(lr); spill_list.Remove(scratch0); // Scratch registers don't need to be preserved. + spill_list.Combine(lr); + spill_list.Combine(padreg); // Add padreg to make the list of even length. + DCHECK_EQ(spill_list.Count() % 2, 0); __ PushCPURegList(spill_list); @@ -1811,22 +1835,20 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kHolderIndex == 0); - // new target - __ PushRoot(Heap::kUndefinedValueRootIndex); + Register undef = x7; + __ LoadRoot(undef, Heap::kUndefinedValueRootIndex); - // context, callee and call data. - __ Push(context, callee, call_data); + // Push new target, context, callee and call data. + __ Push(undef, context, callee, call_data); - Register scratch = call_data; - __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex); Register isolate_reg = x5; __ Mov(isolate_reg, ExternalReference::isolate_address(masm->isolate())); // FunctionCallbackArguments: // return value, return value default, isolate, holder. - __ Push(scratch, scratch, isolate_reg, holder); + __ Push(undef, undef, isolate_reg, holder); - // Enter a new context + // Enter a new context. if (is_lazy()) { // ----------- S t a t e ------------------------------------- // -- sp[0] : holder @@ -1839,8 +1861,9 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { // -- sp[(FCA::kArgsLength + argc + 1) * 8] : accessor_holder // ----------------------------------------------------------- - // Load context from accessor_holder + // Load context from accessor_holder. Register accessor_holder = context; + Register scratch = undef; Register scratch2 = callee; __ Ldr(accessor_holder, MemOperand(__ StackPointer(), @@ -1852,10 +1875,10 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { __ Tst(scratch2, Operand(1 << Map::kIsConstructor)); __ B(ne, &skip_looking_for_constructor); __ GetMapConstructor(context, scratch, scratch, scratch2); - __ bind(&skip_looking_for_constructor); + __ Bind(&skip_looking_for_constructor); __ Ldr(context, FieldMemOperand(context, JSFunction::kContextOffset)); } else { - // Load context from callee + // Load context from callee. __ Ldr(context, FieldMemOperand(callee, JSFunction::kContextOffset)); } diff --git a/src/arm64/macro-assembler-arm64-inl.h b/src/arm64/macro-assembler-arm64-inl.h index 72676acebd..e40f71c04d 100644 --- a/src/arm64/macro-assembler-arm64-inl.h +++ b/src/arm64/macro-assembler-arm64-inl.h @@ -1355,6 +1355,9 @@ void TurboAssembler::Drop(const Register& count, uint64_t unit_size) { } } +void TurboAssembler::DropArguments(const Register& count, uint64_t unit_size) { + Drop(count, unit_size); +} void MacroAssembler::DropBySMI(const Register& count_smi, uint64_t unit_size) { DCHECK(unit_size == 0 || base::bits::IsPowerOfTwo(unit_size)); diff --git a/src/arm64/macro-assembler-arm64.cc b/src/arm64/macro-assembler-arm64.cc index b23b5d88df..5fb7a6bb81 100644 --- a/src/arm64/macro-assembler-arm64.cc +++ b/src/arm64/macro-assembler-arm64.cc @@ -2440,10 +2440,8 @@ void TurboAssembler::EnterFrame(StackFrame::Type type) { if (type == StackFrame::INTERNAL) { DCHECK(jssp.Is(StackPointer())); Mov(type_reg, StackFrame::TypeToMarker(type)); - Push(lr, fp); - Push(type_reg); Mov(code_reg, Operand(CodeObject())); - Push(code_reg); + Push(lr, fp, type_reg, code_reg); Add(fp, jssp, InternalFrameConstants::kFixedFrameSizeFromFp); // jssp[4] : lr // jssp[3] : fp @@ -2454,7 +2452,7 @@ void TurboAssembler::EnterFrame(StackFrame::Type type) { Mov(type_reg, StackFrame::TypeToMarker(type)); Push(lr, fp); Mov(fp, csp); - Push(type_reg, xzr); + Push(type_reg, padreg); // csp[3] : lr // csp[2] : fp // csp[1] : type @@ -2462,8 +2460,7 @@ void TurboAssembler::EnterFrame(StackFrame::Type type) { } else { DCHECK(jssp.Is(StackPointer())); Mov(type_reg, StackFrame::TypeToMarker(type)); - Push(lr, fp); - Push(type_reg); + Push(lr, fp, type_reg); Add(fp, jssp, TypedFrameConstants::kFixedFrameSizeFromFp); // jssp[2] : lr // jssp[1] : fp @@ -2663,34 +2660,6 @@ void MacroAssembler::MaybeDropFrames() { ne); } -void MacroAssembler::PushStackHandler() { - DCHECK(jssp.Is(StackPointer())); - // Adjust this code if the asserts don't hold. - STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - - // For the JSEntry handler, we must preserve the live registers x0-x4. - // (See JSEntryStub::GenerateBody().) - - // Link the current handler as the next handler. - Mov(x11, ExternalReference(IsolateAddressId::kHandlerAddress, isolate())); - Ldr(x10, MemOperand(x11)); - Push(x10); - - // Set this new handler as the current one. - Str(jssp, MemOperand(x11)); -} - - -void MacroAssembler::PopStackHandler() { - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - Pop(x10); - Mov(x11, ExternalReference(IsolateAddressId::kHandlerAddress, isolate())); - Drop(StackHandlerConstants::kSize - kXRegSize, kByteSizeInBytes); - Str(x10, MemOperand(x11)); -} - - void MacroAssembler::Allocate(int object_size, Register result, Register scratch1, @@ -2922,14 +2891,6 @@ void MacroAssembler::GetMapConstructor(Register result, Register map, Bind(&done); } -void MacroAssembler::PushRoot(Heap::RootListIndex index) { - UseScratchRegisterScope temps(this); - Register temp = temps.AcquireX(); - LoadRoot(temp, index); - Push(temp); -} - - void MacroAssembler::CompareRoot(const Register& obj, Heap::RootListIndex index) { UseScratchRegisterScope temps(this); @@ -3049,6 +3010,9 @@ void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. void MacroAssembler::PopSafepointRegisters() { const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; + DCHECK_GE(num_unsaved, 0); + DCHECK_EQ(num_unsaved % 2, 0); + DCHECK_EQ(kSafepointSavedRegisters % 2, 0); PopXRegList(kSafepointSavedRegisters); Drop(num_unsaved); } @@ -3058,7 +3022,9 @@ void MacroAssembler::PushSafepointRegisters() { // Safepoints expect a block of kNumSafepointRegisters values on the stack, so // adjust the stack for unsaved registers. const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; - DCHECK(num_unsaved >= 0); + DCHECK_GE(num_unsaved, 0); + DCHECK_EQ(num_unsaved % 2, 0); + DCHECK_EQ(kSafepointSavedRegisters % 2, 0); Claim(num_unsaved); PushXRegList(kSafepointSavedRegisters); } @@ -3219,14 +3185,14 @@ void MacroAssembler::RecordWriteForMap(Register object, // Record the actual write. if (lr_status == kLRHasNotBeenSaved) { - Push(lr); + Push(padreg, lr); } Add(dst, object, HeapObject::kMapOffset - kHeapObjectTag); RecordWriteStub stub(isolate(), object, map, dst, OMIT_REMEMBERED_SET, fp_mode); CallStub(&stub); if (lr_status == kLRHasNotBeenSaved) { - Pop(lr); + Pop(lr, padreg); } Bind(&done); @@ -3293,13 +3259,13 @@ void MacroAssembler::RecordWrite( // Record the actual write. if (lr_status == kLRHasNotBeenSaved) { - Push(lr); + Push(padreg, lr); } RecordWriteStub stub(isolate(), object, value, address, remembered_set_action, fp_mode); CallStub(&stub); if (lr_status == kLRHasNotBeenSaved) { - Pop(lr); + Pop(lr, padreg); } Bind(&done); diff --git a/src/arm64/macro-assembler-arm64.h b/src/arm64/macro-assembler-arm64.h index 1ecc09bee0..74edac920f 100644 --- a/src/arm64/macro-assembler-arm64.h +++ b/src/arm64/macro-assembler-arm64.h @@ -702,6 +702,14 @@ class TurboAssembler : public Assembler { inline void Drop(int64_t count, uint64_t unit_size = kXRegSize); inline void Drop(const Register& count, uint64_t unit_size = kXRegSize); + // Drop arguments from stack without actually accessing memory. + // This will currently drop 'count' arguments of the given size from the + // stack. + // TODO(arm64): Update this to round up the number of bytes dropped to + // a multiple of 16, so that we can remove jssp. + inline void DropArguments(const Register& count, + uint64_t unit_size = kXRegSize); + // Re-synchronizes the system stack pointer (csp) with the current stack // pointer (according to StackPointer()). // @@ -1874,16 +1882,6 @@ class MacroAssembler : public TurboAssembler { // Frame restart support void MaybeDropFrames(); - // Exception handling - - // Push a new stack handler and link into stack handler chain. - void PushStackHandler(); - - // Unlink the stack handler on top of the stack from the stack handler chain. - // Must preserve the result register. - void PopStackHandler(); - - // --------------------------------------------------------------------------- // Allocation support @@ -2001,9 +1999,6 @@ class MacroAssembler : public TurboAssembler { // register. void LoadElementsKindFromMap(Register result, Register map); - // Load the value from the root list and push it onto the stack. - void PushRoot(Heap::RootListIndex index); - // Compare the object in a register to a value from the root list. void CompareRoot(const Register& obj, Heap::RootListIndex index); @@ -2292,11 +2287,6 @@ class MacroAssembler : public TurboAssembler { const CPURegister& arg2 = NoCPUReg, const CPURegister& arg3 = NoCPUReg); - // Return true if the sequence is a young sequence geneated by - // EmitFrameSetupForCodeAgePatching. Otherwise, this method asserts that the - // sequence is a code age sequence (emitted by EmitCodeAgeSequence). - static bool IsYoungSequence(Isolate* isolate, byte* sequence); - private: // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. void InNewSpace(Register object, diff --git a/src/builtins/arm64/builtins-arm64.cc b/src/builtins/arm64/builtins-arm64.cc index 4a79a44c58..efdce9bddd 100644 --- a/src/builtins/arm64/builtins-arm64.cc +++ b/src/builtins/arm64/builtins-arm64.cc @@ -203,12 +203,10 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::CONSTRUCT); + __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); // Preserve the incoming parameters on the stack. - __ SmiTag(x0); - __ Push(cp, x0); - __ SmiUntag(x0); - - __ PushRoot(Heap::kTheHoleValueRootIndex); + __ SmiTag(x11, x0); + __ Push(cp, x11, x10); // Set up pointer to last argument. __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset); @@ -1111,14 +1109,15 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( Label stack_overflow; // Add one for the receiver. - __ add(x3, x0, Operand(1)); + __ Add(x3, x0, 1); // Add a stack check before pushing arguments. Generate_StackOverflowCheck(masm, x3, x6, &stack_overflow); // Push "undefined" as the receiver arg if we need to. if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { - __ PushRoot(Heap::kUndefinedValueRootIndex); + __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); + __ Push(x10); __ Mov(x3, x0); // Argument count is correct. } @@ -1451,62 +1450,88 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { // -- x1 : new target (preserved for callee) // -- x3 : target function (preserved for callee) // ----------------------------------- + Register argc = x0; + Register new_target = x1; + Register target = x3; + Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve argument count for later compare. - __ Move(x4, x0); - // Push a copy of the target function and the new target. - __ SmiTag(x0); - // Push another copy as a parameter to the runtime call. - __ Push(x0, x1, x3, x1); - // Copy arguments from caller (stdlib, foreign, heap). + // Push argument count, a copy of the target function and the new target, + // together with some padding to maintain 16-byte alignment. + __ SmiTag(argc); + __ Push(argc, new_target, target, padreg); + + // Push another copy of new target as a parameter to the runtime call and + // copy the rest of the arguments from caller (stdlib, foreign, heap). Label args_done; - for (int j = 0; j < 4; ++j) { - Label over; - if (j < 3) { - __ cmp(x4, Operand(j)); - __ B(ne, &over); - } - for (int i = j - 1; i >= 0; --i) { - __ ldr(x4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(x4); - } - for (int i = 0; i < 3 - j; ++i) { - __ PushRoot(Heap::kUndefinedValueRootIndex); - } - if (j < 3) { - __ jmp(&args_done); - __ bind(&over); - } - } - __ bind(&args_done); + Register undef = x10; + Register scratch1 = x12; + Register scratch2 = x13; + Register scratch3 = x14; + __ LoadRoot(undef, Heap::kUndefinedValueRootIndex); + + Label at_least_one_arg; + Label three_args; + DCHECK(Smi::kZero == 0); + __ Cbnz(argc, &at_least_one_arg); + + // No arguments. + __ Push(new_target, undef, undef, undef); + __ B(&args_done); + + __ Bind(&at_least_one_arg); + // Load two arguments, though we may only use one (for the one arg case). + __ Ldp(scratch2, scratch1, + MemOperand(fp, StandardFrameConstants::kCallerSPOffset)); + + // Set flags for determining the value of smi-tagged argc. + // lt => 1, eq => 2, gt => 3. + __ Cmp(argc, Smi::FromInt(2)); + __ B(gt, &three_args); + + // One or two arguments. + // If there is one argument (flags are lt), scratch2 contains that argument, + // and scratch1 must be undefined. + __ CmovX(scratch1, scratch2, lt); + __ CmovX(scratch2, undef, lt); + __ Push(new_target, scratch1, scratch2, undef); + __ B(&args_done); + + // Three arguments. + __ Bind(&three_args); + __ Ldr(scratch3, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + 2 * kPointerSize)); + __ Push(new_target, scratch3, scratch1, scratch2); + + __ Bind(&args_done); // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); + // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(x0, &failed); - __ Drop(2); - __ pop(x4); - __ SmiUntag(x4); + // Peek the argument count from the stack, untagging at the same time. + __ Ldr(w4, UntagSmiMemOperand(__ StackPointer(), 3 * kPointerSize)); + __ Drop(4); scope.GenerateLeaveFrame(); - __ add(x4, x4, Operand(1)); - __ Drop(x4); + // Drop arguments and receiver. + __ Add(x4, x4, 1); + __ DropArguments(x4); __ Ret(); - __ bind(&failed); + __ Bind(&failed); // Restore target function and new target. - __ Pop(x3, x1, x0); - __ SmiUntag(x0); + __ Pop(padreg, target, new_target, argc); + __ SmiUntag(argc); } // On failure, tail call back to regular js by re-calling the function // which has be reset to the compile lazy builtin. - __ Ldr(x4, FieldMemOperand(x1, JSFunction::kCodeOffset)); - __ Add(x4, x4, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Ldr(x4, FieldMemOperand(new_target, JSFunction::kCodeOffset)); + __ Add(x4, x4, Code::kHeaderSize - kHeapObjectTag); __ Jump(x4); } @@ -2164,14 +2189,12 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, // in the fast case? (fall back to AllocateInNewSpace?) FrameScope scope(masm, StackFrame::INTERNAL); __ SmiTag(x0); - __ Push(x0, x1); + __ Push(padreg, x0, x1, cp); __ Mov(x0, x3); - __ Push(cp); __ Call(BUILTIN_CODE(masm->isolate(), ToObject), RelocInfo::CODE_TARGET); - __ Pop(cp); __ Mov(x3, x0); - __ Pop(x1, x0); + __ Pop(cp, x1, x0, padreg); __ SmiUntag(x0); } __ Ldr(x2, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); @@ -2195,10 +2218,10 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ InvokeFunctionCode(x1, no_reg, expected, actual, JUMP_FUNCTION); // The function is a "classConstructor", need to raise an exception. - __ bind(&class_constructor); + __ Bind(&class_constructor); { FrameScope frame(masm, StackFrame::INTERNAL); - __ Push(x1); + __ Push(padreg, x1); __ CallRuntime(Runtime::kThrowConstructorNonCallableError); } }