Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(355)

Side by Side Diff: src/arm64/macro-assembler-arm64.cc

Issue 268353005: ARM64: Fix and improve MacroAssembler::Printf. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Reorganise Simulator::DoPrintf as suggested. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/arm64/macro-assembler-arm64.h ('k') | src/arm64/simulator-arm64.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "v8.h" 5 #include "v8.h"
6 6
7 #if V8_TARGET_ARCH_ARM64 7 #if V8_TARGET_ARCH_ARM64
8 8
9 #include "bootstrapper.h" 9 #include "bootstrapper.h"
10 #include "codegen.h" 10 #include "codegen.h"
(...skipping 4848 matching lines...) Expand 10 before | Expand all | Expand 10 after
4859 // PrintfNoPreserve after setting up one or more PreserveRegisterScopes. 4859 // PrintfNoPreserve after setting up one or more PreserveRegisterScopes.
4860 void MacroAssembler::PrintfNoPreserve(const char * format, 4860 void MacroAssembler::PrintfNoPreserve(const char * format,
4861 const CPURegister& arg0, 4861 const CPURegister& arg0,
4862 const CPURegister& arg1, 4862 const CPURegister& arg1,
4863 const CPURegister& arg2, 4863 const CPURegister& arg2,
4864 const CPURegister& arg3) { 4864 const CPURegister& arg3) {
4865 // We cannot handle a caller-saved stack pointer. It doesn't make much sense 4865 // We cannot handle a caller-saved stack pointer. It doesn't make much sense
4866 // in most cases anyway, so this restriction shouldn't be too serious. 4866 // in most cases anyway, so this restriction shouldn't be too serious.
4867 ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer())); 4867 ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer()));
4868 4868
4869 // Make sure that the macro assembler doesn't try to use any of our arguments 4869 // The provided arguments, and their proper procedure-call standard registers.
4870 // as scratch registers. 4870 CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3};
4871 ASSERT(!TmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3)); 4871 CPURegister pcs[kPrintfMaxArgCount] = {NoReg, NoReg, NoReg, NoReg};
4872 ASSERT(!FPTmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3));
4873 4872
4874 // We cannot print the stack pointer because it is typically used to preserve 4873 int arg_count = kPrintfMaxArgCount;
4875 // caller-saved registers (using other Printf variants which depend on this
4876 // helper).
4877 ASSERT(!AreAliased(arg0, StackPointer()));
4878 ASSERT(!AreAliased(arg1, StackPointer()));
4879 ASSERT(!AreAliased(arg2, StackPointer()));
4880 ASSERT(!AreAliased(arg3, StackPointer()));
4881 4874
4882 static const int kMaxArgCount = 4; 4875 // The PCS varargs registers for printf. Note that x0 is used for the printf
4883 // Assume that we have the maximum number of arguments until we know 4876 // format string.
4884 // otherwise. 4877 static const CPURegList kPCSVarargs =
4885 int arg_count = kMaxArgCount; 4878 CPURegList(CPURegister::kRegister, kXRegSizeInBits, 1, arg_count);
4879 static const CPURegList kPCSVarargsFP =
4880 CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 0, arg_count - 1);
4886 4881
4887 // The provided arguments. 4882 // We can use caller-saved registers as scratch values, except for the
4888 CPURegister args[kMaxArgCount] = {arg0, arg1, arg2, arg3}; 4883 // arguments and the PCS registers where they might need to go.
4884 CPURegList tmp_list = kCallerSaved;
4885 tmp_list.Remove(x0); // Used to pass the format string.
4886 tmp_list.Remove(kPCSVarargs);
4887 tmp_list.Remove(arg0, arg1, arg2, arg3);
4889 4888
4890 // The PCS registers where the arguments need to end up. 4889 CPURegList fp_tmp_list = kCallerSavedFP;
4891 CPURegister pcs[kMaxArgCount] = {NoCPUReg, NoCPUReg, NoCPUReg, NoCPUReg}; 4890 fp_tmp_list.Remove(kPCSVarargsFP);
4891 fp_tmp_list.Remove(arg0, arg1, arg2, arg3);
4892 4892
4893 // Promote FP arguments to doubles, and integer arguments to X registers. 4893 // Override the MacroAssembler's scratch register list. The lists will be
4894 // Note that FP and integer arguments cannot be mixed, but we'll check 4894 // reset automatically at the end of the UseScratchRegisterScope.
4895 // AreSameSizeAndType once we've processed these promotions. 4895 UseScratchRegisterScope temps(this);
4896 for (int i = 0; i < kMaxArgCount; i++) { 4896 TmpList()->set_list(tmp_list.list());
4897 FPTmpList()->set_list(fp_tmp_list.list());
4898
4899 // Copies of the printf vararg registers that we can pop from.
4900 CPURegList pcs_varargs = kPCSVarargs;
4901 CPURegList pcs_varargs_fp = kPCSVarargsFP;
4902
4903 // Place the arguments. There are lots of clever tricks and optimizations we
4904 // could use here, but Printf is a debug tool so instead we just try to keep
4905 // it simple: Move each input that isn't already in the right place to a
4906 // scratch register, then move everything back.
4907 for (unsigned i = 0; i < kPrintfMaxArgCount; i++) {
4908 // Work out the proper PCS register for this argument.
4897 if (args[i].IsRegister()) { 4909 if (args[i].IsRegister()) {
4898 // Note that we use x1 onwards, because x0 will hold the format string. 4910 pcs[i] = pcs_varargs.PopLowestIndex().X();
4899 pcs[i] = Register::XRegFromCode(i + 1); 4911 // We might only need a W register here. We need to know the size of the
4900 // For simplicity, we handle all integer arguments as X registers. An X 4912 // argument so we can properly encode it for the simulator call.
4901 // register argument takes the same space as a W register argument in the 4913 if (args[i].Is32Bits()) pcs[i] = pcs[i].W();
4902 // PCS anyway. The only limitation is that we must explicitly clear the
4903 // top word for W register arguments as the callee will expect it to be
4904 // clear.
4905 if (!args[i].Is64Bits()) {
4906 const Register& as_x = args[i].X();
4907 And(as_x, as_x, 0x00000000ffffffff);
4908 args[i] = as_x;
4909 }
4910 } else if (args[i].IsFPRegister()) { 4914 } else if (args[i].IsFPRegister()) {
4911 pcs[i] = FPRegister::DRegFromCode(i); 4915 // In C, floats are always cast to doubles for varargs calls.
4912 // C and C++ varargs functions (such as printf) implicitly promote float 4916 pcs[i] = pcs_varargs_fp.PopLowestIndex().D();
4913 // arguments to doubles.
4914 if (!args[i].Is64Bits()) {
4915 FPRegister s(args[i]);
4916 const FPRegister& as_d = args[i].D();
4917 Fcvt(as_d, s);
4918 args[i] = as_d;
4919 }
4920 } else { 4917 } else {
4921 // This is the first empty (NoCPUReg) argument, so use it to set the 4918 ASSERT(args[i].IsNone());
4922 // argument count and bail out.
4923 arg_count = i; 4919 arg_count = i;
4924 break; 4920 break;
4925 } 4921 }
4926 }
4927 ASSERT((arg_count >= 0) && (arg_count <= kMaxArgCount));
4928 // Check that every remaining argument is NoCPUReg.
4929 for (int i = arg_count; i < kMaxArgCount; i++) {
4930 ASSERT(args[i].IsNone());
4931 }
4932 ASSERT((arg_count == 0) || AreSameSizeAndType(args[0], args[1],
4933 args[2], args[3],
4934 pcs[0], pcs[1],
4935 pcs[2], pcs[3]));
4936 4922
4937 // Move the arguments into the appropriate PCS registers. 4923 // If the argument is already in the right place, leave it where it is.
4938 // 4924 if (args[i].Aliases(pcs[i])) continue;
4939 // Arranging an arbitrary list of registers into x1-x4 (or d0-d3) is
4940 // surprisingly complicated.
4941 //
4942 // * For even numbers of registers, we push the arguments and then pop them
4943 // into their final registers. This maintains 16-byte stack alignment in
4944 // case csp is the stack pointer, since we're only handling X or D
4945 // registers at this point.
4946 //
4947 // * For odd numbers of registers, we push and pop all but one register in
4948 // the same way, but the left-over register is moved directly, since we
4949 // can always safely move one register without clobbering any source.
4950 if (arg_count >= 4) {
4951 Push(args[3], args[2], args[1], args[0]);
4952 } else if (arg_count >= 2) {
4953 Push(args[1], args[0]);
4954 }
4955 4925
4956 if ((arg_count % 2) != 0) { 4926 // Otherwise, if the argument is in a PCS argument register, allocate an
4957 // Move the left-over register directly. 4927 // appropriate scratch register and then move it out of the way.
4958 const CPURegister& leftover_arg = args[arg_count - 1]; 4928 if (kPCSVarargs.IncludesAliasOf(args[i]) ||
4959 const CPURegister& leftover_pcs = pcs[arg_count - 1]; 4929 kPCSVarargsFP.IncludesAliasOf(args[i])) {
4960 if (leftover_arg.IsRegister()) { 4930 if (args[i].IsRegister()) {
4961 Mov(Register(leftover_pcs), Register(leftover_arg)); 4931 Register old_arg = Register(args[i]);
4962 } else { 4932 Register new_arg = temps.AcquireSameSizeAs(old_arg);
4963 Fmov(FPRegister(leftover_pcs), FPRegister(leftover_arg)); 4933 Mov(new_arg, old_arg);
4934 args[i] = new_arg;
4935 } else {
4936 FPRegister old_arg = FPRegister(args[i]);
4937 FPRegister new_arg = temps.AcquireSameSizeAs(old_arg);
4938 Fmov(new_arg, old_arg);
4939 args[i] = new_arg;
4940 }
4964 } 4941 }
4965 } 4942 }
4966 4943
4967 if (arg_count >= 4) { 4944 // Do a second pass to move values into their final positions and perform any
4968 Pop(pcs[0], pcs[1], pcs[2], pcs[3]); 4945 // conversions that may be required.
4969 } else if (arg_count >= 2) { 4946 for (int i = 0; i < arg_count; i++) {
4970 Pop(pcs[0], pcs[1]); 4947 ASSERT(pcs[i].type() == args[i].type());
4948 if (pcs[i].IsRegister()) {
4949 Mov(Register(pcs[i]), Register(args[i]), kDiscardForSameWReg);
4950 } else {
4951 ASSERT(pcs[i].IsFPRegister());
4952 if (pcs[i].SizeInBytes() == args[i].SizeInBytes()) {
4953 Fmov(FPRegister(pcs[i]), FPRegister(args[i]));
4954 } else {
4955 Fcvt(FPRegister(pcs[i]), FPRegister(args[i]));
4956 }
4957 }
4971 } 4958 }
4972 4959
4973 // Load the format string into x0, as per the procedure-call standard. 4960 // Load the format string into x0, as per the procedure-call standard.
4974 // 4961 //
4975 // To make the code as portable as possible, the format string is encoded 4962 // To make the code as portable as possible, the format string is encoded
4976 // directly in the instruction stream. It might be cleaner to encode it in a 4963 // directly in the instruction stream. It might be cleaner to encode it in a
4977 // literal pool, but since Printf is usually used for debugging, it is 4964 // literal pool, but since Printf is usually used for debugging, it is
4978 // beneficial for it to be minimally dependent on other features. 4965 // beneficial for it to be minimally dependent on other features.
4979 Label format_address; 4966 Label format_address;
4980 Adr(x0, &format_address); 4967 Adr(x0, &format_address);
4981 4968
4982 // Emit the format string directly in the instruction stream. 4969 // Emit the format string directly in the instruction stream.
4983 { BlockPoolsScope scope(this); 4970 { BlockPoolsScope scope(this);
4984 Label after_data; 4971 Label after_data;
4985 B(&after_data); 4972 B(&after_data);
4986 Bind(&format_address); 4973 Bind(&format_address);
4987 EmitStringData(format); 4974 EmitStringData(format);
4988 Unreachable(); 4975 Unreachable();
4989 Bind(&after_data); 4976 Bind(&after_data);
4990 } 4977 }
4991 4978
4992 // We don't pass any arguments on the stack, but we still need to align the C 4979 // We don't pass any arguments on the stack, but we still need to align the C
4993 // stack pointer to a 16-byte boundary for PCS compliance. 4980 // stack pointer to a 16-byte boundary for PCS compliance.
4994 if (!csp.Is(StackPointer())) { 4981 if (!csp.Is(StackPointer())) {
4995 Bic(csp, StackPointer(), 0xf); 4982 Bic(csp, StackPointer(), 0xf);
4996 } 4983 }
4997 4984
4998 CallPrintf(pcs[0].type()); 4985 CallPrintf(arg_count, pcs);
4999 } 4986 }
5000 4987
5001 4988
5002 void MacroAssembler::CallPrintf(CPURegister::RegisterType type) { 4989 void MacroAssembler::CallPrintf(int arg_count, const CPURegister * args) {
5003 // A call to printf needs special handling for the simulator, since the system 4990 // A call to printf needs special handling for the simulator, since the system
5004 // printf function will use a different instruction set and the procedure-call 4991 // printf function will use a different instruction set and the procedure-call
5005 // standard will not be compatible. 4992 // standard will not be compatible.
5006 #ifdef USE_SIMULATOR 4993 #ifdef USE_SIMULATOR
5007 { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize); 4994 { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize);
5008 hlt(kImmExceptionIsPrintf); 4995 hlt(kImmExceptionIsPrintf);
5009 dc32(type); 4996 dc32(arg_count); // kPrintfArgCountOffset
4997
4998 // Determine the argument pattern.
4999 uint32_t arg_pattern_list = 0;
5000 for (int i = 0; i < arg_count; i++) {
5001 uint32_t arg_pattern;
5002 if (args[i].IsRegister()) {
5003 arg_pattern = args[i].Is32Bits() ? kPrintfArgW : kPrintfArgX;
5004 } else {
5005 ASSERT(args[i].Is64Bits());
5006 arg_pattern = kPrintfArgD;
5007 }
5008 ASSERT(arg_pattern < (1 << kPrintfArgPatternBits));
5009 arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i));
5010 }
5011 dc32(arg_pattern_list); // kPrintfArgPatternListOffset
5010 } 5012 }
5011 #else 5013 #else
5012 Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE); 5014 Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE);
5013 #endif 5015 #endif
5014 } 5016 }
5015 5017
5016 5018
5017 void MacroAssembler::Printf(const char * format, 5019 void MacroAssembler::Printf(const char * format,
5018 const CPURegister& arg0, 5020 CPURegister arg0,
5019 const CPURegister& arg1, 5021 CPURegister arg1,
5020 const CPURegister& arg2, 5022 CPURegister arg2,
5021 const CPURegister& arg3) { 5023 CPURegister arg3) {
5024 // We can only print sp if it is the current stack pointer.
5025 if (!csp.Is(StackPointer())) {
5026 ASSERT(!csp.Aliases(arg0));
5027 ASSERT(!csp.Aliases(arg1));
5028 ASSERT(!csp.Aliases(arg2));
5029 ASSERT(!csp.Aliases(arg3));
5030 }
5031
5022 // Printf is expected to preserve all registers, so make sure that none are 5032 // Printf is expected to preserve all registers, so make sure that none are
5023 // available as scratch registers until we've preserved them. 5033 // available as scratch registers until we've preserved them.
5024 RegList old_tmp_list = TmpList()->list(); 5034 RegList old_tmp_list = TmpList()->list();
5025 RegList old_fp_tmp_list = FPTmpList()->list(); 5035 RegList old_fp_tmp_list = FPTmpList()->list();
5026 TmpList()->set_list(0); 5036 TmpList()->set_list(0);
5027 FPTmpList()->set_list(0); 5037 FPTmpList()->set_list(0);
5028 5038
5029 // Preserve all caller-saved registers as well as NZCV. 5039 // Preserve all caller-saved registers as well as NZCV.
5030 // If csp is the stack pointer, PushCPURegList asserts that the size of each 5040 // If csp is the stack pointer, PushCPURegList asserts that the size of each
5031 // list is a multiple of 16 bytes. 5041 // list is a multiple of 16 bytes.
5032 PushCPURegList(kCallerSaved); 5042 PushCPURegList(kCallerSaved);
5033 PushCPURegList(kCallerSavedFP); 5043 PushCPURegList(kCallerSavedFP);
5034 5044
5035 // We can use caller-saved registers as scratch values (except for argN). 5045 // We can use caller-saved registers as scratch values (except for argN).
5036 CPURegList tmp_list = kCallerSaved; 5046 CPURegList tmp_list = kCallerSaved;
5037 CPURegList fp_tmp_list = kCallerSavedFP; 5047 CPURegList fp_tmp_list = kCallerSavedFP;
5038 tmp_list.Remove(arg0, arg1, arg2, arg3); 5048 tmp_list.Remove(arg0, arg1, arg2, arg3);
5039 fp_tmp_list.Remove(arg0, arg1, arg2, arg3); 5049 fp_tmp_list.Remove(arg0, arg1, arg2, arg3);
5040 TmpList()->set_list(tmp_list.list()); 5050 TmpList()->set_list(tmp_list.list());
5041 FPTmpList()->set_list(fp_tmp_list.list()); 5051 FPTmpList()->set_list(fp_tmp_list.list());
5042 5052
5043 // Preserve NZCV.
5044 { UseScratchRegisterScope temps(this); 5053 { UseScratchRegisterScope temps(this);
5045 Register tmp = temps.AcquireX(); 5054 // If any of the arguments are the current stack pointer, allocate a new
5046 Mrs(tmp, NZCV); 5055 // register for them, and adjust the value to compensate for pushing the
5047 Push(tmp, xzr); 5056 // caller-saved registers.
5048 } 5057 bool arg0_sp = StackPointer().Aliases(arg0);
5058 bool arg1_sp = StackPointer().Aliases(arg1);
5059 bool arg2_sp = StackPointer().Aliases(arg2);
5060 bool arg3_sp = StackPointer().Aliases(arg3);
5061 if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) {
5062 // Allocate a register to hold the original stack pointer value, to pass
5063 // to PrintfNoPreserve as an argument.
5064 Register arg_sp = temps.AcquireX();
5065 Add(arg_sp, StackPointer(),
5066 kCallerSaved.TotalSizeInBytes() + kCallerSavedFP.TotalSizeInBytes());
5067 if (arg0_sp) arg0 = Register::Create(arg_sp.code(), arg0.SizeInBits());
5068 if (arg1_sp) arg1 = Register::Create(arg_sp.code(), arg1.SizeInBits());
5069 if (arg2_sp) arg2 = Register::Create(arg_sp.code(), arg2.SizeInBits());
5070 if (arg3_sp) arg3 = Register::Create(arg_sp.code(), arg3.SizeInBits());
5071 }
5049 5072
5050 PrintfNoPreserve(format, arg0, arg1, arg2, arg3); 5073 // Preserve NZCV.
5074 { UseScratchRegisterScope temps(this);
5075 Register tmp = temps.AcquireX();
5076 Mrs(tmp, NZCV);
5077 Push(tmp, xzr);
5078 }
5051 5079
5052 { UseScratchRegisterScope temps(this); 5080 PrintfNoPreserve(format, arg0, arg1, arg2, arg3);
5053 Register tmp = temps.AcquireX(); 5081
5054 Pop(xzr, tmp); 5082 // Restore NZCV.
5055 Msr(NZCV, tmp); 5083 { UseScratchRegisterScope temps(this);
5084 Register tmp = temps.AcquireX();
5085 Pop(xzr, tmp);
5086 Msr(NZCV, tmp);
5087 }
5056 } 5088 }
5057 5089
5058 PopCPURegList(kCallerSavedFP); 5090 PopCPURegList(kCallerSavedFP);
5059 PopCPURegList(kCallerSaved); 5091 PopCPURegList(kCallerSaved);
5060 5092
5061 TmpList()->set_list(old_tmp_list); 5093 TmpList()->set_list(old_tmp_list);
5062 FPTmpList()->set_list(old_fp_tmp_list); 5094 FPTmpList()->set_list(old_fp_tmp_list);
5063 } 5095 }
5064 5096
5065 5097
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
5235 } 5267 }
5236 } 5268 }
5237 5269
5238 5270
5239 #undef __ 5271 #undef __
5240 5272
5241 5273
5242 } } // namespace v8::internal 5274 } } // namespace v8::internal
5243 5275
5244 #endif // V8_TARGET_ARCH_ARM64 5276 #endif // V8_TARGET_ARCH_ARM64
OLDNEW
« no previous file with comments | « src/arm64/macro-assembler-arm64.h ('k') | src/arm64/simulator-arm64.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698