Index: src/arm64/macro-assembler-arm64.cc |
diff --git a/src/arm64/macro-assembler-arm64.cc b/src/arm64/macro-assembler-arm64.cc |
index c5ce99be9924c624b493a166fe5f0da71561700d..49c74b888d6094a14ef8d998755932d5f5adc9db 100644 |
--- a/src/arm64/macro-assembler-arm64.cc |
+++ b/src/arm64/macro-assembler-arm64.cc |
@@ -4866,108 +4866,95 @@ void MacroAssembler::PrintfNoPreserve(const char * format, |
// in most cases anyway, so this restriction shouldn't be too serious. |
ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer())); |
- // Make sure that the macro assembler doesn't try to use any of our arguments |
- // as scratch registers. |
- ASSERT(!TmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3)); |
- ASSERT(!FPTmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3)); |
- |
- // We cannot print the stack pointer because it is typically used to preserve |
- // caller-saved registers (using other Printf variants which depend on this |
- // helper). |
- ASSERT(!AreAliased(arg0, StackPointer())); |
- ASSERT(!AreAliased(arg1, StackPointer())); |
- ASSERT(!AreAliased(arg2, StackPointer())); |
- ASSERT(!AreAliased(arg3, StackPointer())); |
- |
- static const int kMaxArgCount = 4; |
- // Assume that we have the maximum number of arguments until we know |
- // otherwise. |
- int arg_count = kMaxArgCount; |
- |
- // The provided arguments. |
- CPURegister args[kMaxArgCount] = {arg0, arg1, arg2, arg3}; |
- |
- // The PCS registers where the arguments need to end up. |
- CPURegister pcs[kMaxArgCount] = {NoCPUReg, NoCPUReg, NoCPUReg, NoCPUReg}; |
- |
- // Promote FP arguments to doubles, and integer arguments to X registers. |
- // Note that FP and integer arguments cannot be mixed, but we'll check |
- // AreSameSizeAndType once we've processed these promotions. |
- for (int i = 0; i < kMaxArgCount; i++) { |
+ // The provided arguments, and their proper procedure-call standard registers. |
+ CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3}; |
+ CPURegister pcs[kPrintfMaxArgCount] = {NoReg, NoReg, NoReg, NoReg}; |
+ |
+ int arg_count = kPrintfMaxArgCount; |
+ |
+ // The PCS varargs registers for printf. Note that x0 is used for the printf |
+ // format string. |
+ static const CPURegList kPCSVarargs = |
+ CPURegList(CPURegister::kRegister, kXRegSizeInBits, 1, arg_count); |
+ static const CPURegList kPCSVarargsFP = |
+ CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 0, arg_count - 1); |
+ |
+ // We can use caller-saved registers as scratch values, except for the |
+ // arguments and the PCS registers where they might need to go. |
+ CPURegList tmp_list = kCallerSaved; |
+ tmp_list.Remove(x0); // Used to pass the format string. |
+ tmp_list.Remove(kPCSVarargs); |
+ tmp_list.Remove(arg0, arg1, arg2, arg3); |
+ |
+ CPURegList fp_tmp_list = kCallerSavedFP; |
+ fp_tmp_list.Remove(kPCSVarargsFP); |
+ fp_tmp_list.Remove(arg0, arg1, arg2, arg3); |
+ |
+ // Override the MacroAssembler's scratch register list. The lists will be |
+ // reset automatically at the end of the UseScratchRegisterScope. |
+ UseScratchRegisterScope temps(this); |
+ TmpList()->set_list(tmp_list.list()); |
+ FPTmpList()->set_list(fp_tmp_list.list()); |
+ |
+ // Copies of the printf vararg registers that we can pop from. |
+ CPURegList pcs_varargs = kPCSVarargs; |
+ CPURegList pcs_varargs_fp = kPCSVarargsFP; |
+ |
+ // Place the arguments. There are lots of clever tricks and optimizations we |
+ // could use here, but Printf is a debug tool so instead we just try to keep |
+ // it simple: Move each input that isn't already in the right place to a |
+ // scratch register, then move everything back. |
+ for (unsigned i = 0; i < kPrintfMaxArgCount; i++) { |
+ // Work out the proper PCS register for this argument. |
if (args[i].IsRegister()) { |
- // Note that we use x1 onwards, because x0 will hold the format string. |
- pcs[i] = Register::XRegFromCode(i + 1); |
- // For simplicity, we handle all integer arguments as X registers. An X |
- // register argument takes the same space as a W register argument in the |
- // PCS anyway. The only limitation is that we must explicitly clear the |
- // top word for W register arguments as the callee will expect it to be |
- // clear. |
- if (!args[i].Is64Bits()) { |
- const Register& as_x = args[i].X(); |
- And(as_x, as_x, 0x00000000ffffffff); |
- args[i] = as_x; |
- } |
+ pcs[i] = pcs_varargs.PopLowestIndex().X(); |
+ // We might only need a W register here. We need to know the size of the |
+ // argument so we can properly encode it for the simulator call. |
+ if (args[i].Is32Bits()) pcs[i] = pcs[i].W(); |
} else if (args[i].IsFPRegister()) { |
- pcs[i] = FPRegister::DRegFromCode(i); |
- // C and C++ varargs functions (such as printf) implicitly promote float |
- // arguments to doubles. |
- if (!args[i].Is64Bits()) { |
- FPRegister s(args[i]); |
- const FPRegister& as_d = args[i].D(); |
- Fcvt(as_d, s); |
- args[i] = as_d; |
- } |
+ // In C, floats are always cast to doubles for varargs calls. |
+ pcs[i] = pcs_varargs_fp.PopLowestIndex().D(); |
} else { |
- // This is the first empty (NoCPUReg) argument, so use it to set the |
- // argument count and bail out. |
+ ASSERT(args[i].IsNone()); |
arg_count = i; |
break; |
} |
- } |
- ASSERT((arg_count >= 0) && (arg_count <= kMaxArgCount)); |
- // Check that every remaining argument is NoCPUReg. |
- for (int i = arg_count; i < kMaxArgCount; i++) { |
- ASSERT(args[i].IsNone()); |
- } |
- ASSERT((arg_count == 0) || AreSameSizeAndType(args[0], args[1], |
- args[2], args[3], |
- pcs[0], pcs[1], |
- pcs[2], pcs[3])); |
- // Move the arguments into the appropriate PCS registers. |
- // |
- // Arranging an arbitrary list of registers into x1-x4 (or d0-d3) is |
- // surprisingly complicated. |
- // |
- // * For even numbers of registers, we push the arguments and then pop them |
- // into their final registers. This maintains 16-byte stack alignment in |
- // case csp is the stack pointer, since we're only handling X or D |
- // registers at this point. |
- // |
- // * For odd numbers of registers, we push and pop all but one register in |
- // the same way, but the left-over register is moved directly, since we |
- // can always safely move one register without clobbering any source. |
- if (arg_count >= 4) { |
- Push(args[3], args[2], args[1], args[0]); |
- } else if (arg_count >= 2) { |
- Push(args[1], args[0]); |
- } |
- |
- if ((arg_count % 2) != 0) { |
- // Move the left-over register directly. |
- const CPURegister& leftover_arg = args[arg_count - 1]; |
- const CPURegister& leftover_pcs = pcs[arg_count - 1]; |
- if (leftover_arg.IsRegister()) { |
- Mov(Register(leftover_pcs), Register(leftover_arg)); |
- } else { |
- Fmov(FPRegister(leftover_pcs), FPRegister(leftover_arg)); |
+ // If the argument is already in the right place, leave it where it is. |
+ if (args[i].Aliases(pcs[i])) continue; |
+ |
+ // Otherwise, if the argument is in a PCS argument register, allocate an |
+ // appropriate scratch register and then move it out of the way. |
+ if (kPCSVarargs.IncludesAliasOf(args[i]) || |
+ kPCSVarargsFP.IncludesAliasOf(args[i])) { |
+ if (args[i].IsRegister()) { |
+ Register old_arg = Register(args[i]); |
+ Register new_arg = temps.AcquireSameSizeAs(old_arg); |
+ Mov(new_arg, old_arg); |
+ args[i] = new_arg; |
+ } else { |
+ FPRegister old_arg = FPRegister(args[i]); |
+ FPRegister new_arg = temps.AcquireSameSizeAs(old_arg); |
+ Fmov(new_arg, old_arg); |
+ args[i] = new_arg; |
+ } |
} |
} |
- if (arg_count >= 4) { |
- Pop(pcs[0], pcs[1], pcs[2], pcs[3]); |
- } else if (arg_count >= 2) { |
- Pop(pcs[0], pcs[1]); |
+ // Do a second pass to move values into their final positions and perform any |
+ // conversions that may be required. |
+ for (int i = 0; i < arg_count; i++) { |
+ ASSERT(pcs[i].type() == args[i].type()); |
+ if (pcs[i].IsRegister()) { |
+ Mov(Register(pcs[i]), Register(args[i]), kDiscardForSameWReg); |
+ } else { |
+ ASSERT(pcs[i].IsFPRegister()); |
+ if (pcs[i].SizeInBytes() == args[i].SizeInBytes()) { |
+ Fmov(FPRegister(pcs[i]), FPRegister(args[i])); |
+ } else { |
+ Fcvt(FPRegister(pcs[i]), FPRegister(args[i])); |
+ } |
+ } |
} |
// Load the format string into x0, as per the procedure-call standard. |
@@ -4995,18 +4982,33 @@ void MacroAssembler::PrintfNoPreserve(const char * format, |
Bic(csp, StackPointer(), 0xf); |
} |
- CallPrintf(pcs[0].type()); |
+ CallPrintf(arg_count, pcs); |
} |
-void MacroAssembler::CallPrintf(CPURegister::RegisterType type) { |
+void MacroAssembler::CallPrintf(int arg_count, const CPURegister * args) { |
// A call to printf needs special handling for the simulator, since the system |
// printf function will use a different instruction set and the procedure-call |
// standard will not be compatible. |
#ifdef USE_SIMULATOR |
{ InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize); |
hlt(kImmExceptionIsPrintf); |
- dc32(type); |
+ dc32(arg_count); // kPrintfArgCountOffset |
+ |
+ // Determine the argument pattern. |
+ uint32_t arg_pattern_list = 0; |
+ for (int i = 0; i < arg_count; i++) { |
+ uint32_t arg_pattern; |
+ if (args[i].IsRegister()) { |
+ arg_pattern = args[i].Is32Bits() ? kPrintfArgW : kPrintfArgX; |
+ } else { |
+ ASSERT(args[i].Is64Bits()); |
+ arg_pattern = kPrintfArgD; |
+ } |
+ ASSERT(arg_pattern < (1 << kPrintfArgPatternBits)); |
+ arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i)); |
+ } |
+ dc32(arg_pattern_list); // kPrintfArgPatternListOffset |
} |
#else |
Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE); |
@@ -5015,10 +5017,18 @@ void MacroAssembler::CallPrintf(CPURegister::RegisterType type) { |
void MacroAssembler::Printf(const char * format, |
- const CPURegister& arg0, |
- const CPURegister& arg1, |
- const CPURegister& arg2, |
- const CPURegister& arg3) { |
+ CPURegister arg0, |
+ CPURegister arg1, |
+ CPURegister arg2, |
+ CPURegister arg3) { |
+ // We can only print sp if it is the current stack pointer. |
+ if (!csp.Is(StackPointer())) { |
+ ASSERT(!csp.Aliases(arg0)); |
+ ASSERT(!csp.Aliases(arg1)); |
+ ASSERT(!csp.Aliases(arg2)); |
+ ASSERT(!csp.Aliases(arg3)); |
+ } |
+ |
// Printf is expected to preserve all registers, so make sure that none are |
// available as scratch registers until we've preserved them. |
RegList old_tmp_list = TmpList()->list(); |
@@ -5040,19 +5050,41 @@ void MacroAssembler::Printf(const char * format, |
TmpList()->set_list(tmp_list.list()); |
FPTmpList()->set_list(fp_tmp_list.list()); |
- // Preserve NZCV. |
{ UseScratchRegisterScope temps(this); |
- Register tmp = temps.AcquireX(); |
- Mrs(tmp, NZCV); |
- Push(tmp, xzr); |
- } |
+ // If any of the arguments are the current stack pointer, allocate a new |
+ // register for them, and adjust the value to compensate for pushing the |
+ // caller-saved registers. |
+ bool arg0_sp = StackPointer().Aliases(arg0); |
+ bool arg1_sp = StackPointer().Aliases(arg1); |
+ bool arg2_sp = StackPointer().Aliases(arg2); |
+ bool arg3_sp = StackPointer().Aliases(arg3); |
+ if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) { |
+ // Allocate a register to hold the original stack pointer value, to pass |
+ // to PrintfNoPreserve as an argument. |
+ Register arg_sp = temps.AcquireX(); |
+ Add(arg_sp, StackPointer(), |
+ kCallerSaved.TotalSizeInBytes() + kCallerSavedFP.TotalSizeInBytes()); |
+ if (arg0_sp) arg0 = Register::Create(arg_sp.code(), arg0.SizeInBits()); |
+ if (arg1_sp) arg1 = Register::Create(arg_sp.code(), arg1.SizeInBits()); |
+ if (arg2_sp) arg2 = Register::Create(arg_sp.code(), arg2.SizeInBits()); |
+ if (arg3_sp) arg3 = Register::Create(arg_sp.code(), arg3.SizeInBits()); |
+ } |
- PrintfNoPreserve(format, arg0, arg1, arg2, arg3); |
+ // Preserve NZCV. |
+ { UseScratchRegisterScope temps(this); |
+ Register tmp = temps.AcquireX(); |
+ Mrs(tmp, NZCV); |
+ Push(tmp, xzr); |
+ } |
- { UseScratchRegisterScope temps(this); |
- Register tmp = temps.AcquireX(); |
- Pop(xzr, tmp); |
- Msr(NZCV, tmp); |
+ PrintfNoPreserve(format, arg0, arg1, arg2, arg3); |
+ |
+ // Restore NZCV. |
+ { UseScratchRegisterScope temps(this); |
+ Register tmp = temps.AcquireX(); |
+ Pop(xzr, tmp); |
+ Msr(NZCV, tmp); |
+ } |
} |
PopCPURegList(kCallerSavedFP); |