OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |