OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/codegen.h" |
| 6 #include "src/compiler/linkage.h" |
| 7 #include "src/compiler/machine-type.h" |
| 8 #include "src/compiler/raw-machine-assembler.h" |
| 9 |
| 10 #include "test/cctest/cctest.h" |
| 11 #include "test/cctest/compiler/codegen-tester.h" |
| 12 #include "test/cctest/compiler/graph-builder-tester.h" |
| 13 #include "test/cctest/compiler/value-helper.h" |
| 14 |
| 15 using namespace v8::base; |
| 16 using namespace v8::internal; |
| 17 using namespace v8::internal::compiler; |
| 18 |
| 19 typedef RawMachineAssembler::Label MLabel; |
| 20 |
| 21 |
| 22 #if V8_TURBOFAN_TARGET |
| 23 |
| 24 namespace { |
| 25 // Helper for allocating either an GP or FP reg, or the next stack slot. |
| 26 struct Allocator { |
| 27 Allocator(int* gp, int gpc, int* fp, int fpc) |
| 28 : gp_count(gpc), |
| 29 gp_offset(0), |
| 30 gp_regs(gp), |
| 31 fp_count(fpc), |
| 32 fp_offset(0), |
| 33 fp_regs(fp), |
| 34 stack_offset(0) {} |
| 35 |
| 36 int gp_count; |
| 37 int gp_offset; |
| 38 int* gp_regs; |
| 39 |
| 40 int fp_count; |
| 41 int fp_offset; |
| 42 int* fp_regs; |
| 43 |
| 44 int stack_offset; |
| 45 |
| 46 LinkageLocation Next(MachineType type) { |
| 47 if (IsFloatingPoint(type)) { |
| 48 // Allocate a floating point register/stack location. |
| 49 if (fp_offset < fp_count) { |
| 50 return LinkageLocation::ForRegister(fp_regs[fp_offset++]); |
| 51 } else { |
| 52 int offset = -1 - stack_offset; |
| 53 stack_offset += Words(type); |
| 54 return LinkageLocation::ForCallerFrameSlot(offset); |
| 55 } |
| 56 } else { |
| 57 // Allocate a general purpose register/stack location. |
| 58 if (gp_offset < gp_count) { |
| 59 return LinkageLocation::ForRegister(gp_regs[gp_offset++]); |
| 60 } else { |
| 61 int offset = -1 - stack_offset; |
| 62 stack_offset += Words(type); |
| 63 return LinkageLocation::ForCallerFrameSlot(offset); |
| 64 } |
| 65 } |
| 66 } |
| 67 bool IsFloatingPoint(MachineType type) { |
| 68 return RepresentationOf(type) == kRepFloat32 || |
| 69 RepresentationOf(type) == kRepFloat64; |
| 70 } |
| 71 int Words(MachineType type) { |
| 72 int size = ElementSizeOf(type); |
| 73 return size <= kPointerSize ? 1 : size / kPointerSize; |
| 74 } |
| 75 void Reset() { |
| 76 fp_offset = 0; |
| 77 gp_offset = 0; |
| 78 stack_offset = 0; |
| 79 } |
| 80 }; |
| 81 |
| 82 |
| 83 class RegisterConfig { |
| 84 public: |
| 85 RegisterConfig(Allocator& p, Allocator& r) : params(p), rets(r) {} |
| 86 |
| 87 CallDescriptor* Create(Zone* zone, MachineSignature* msig) { |
| 88 rets.Reset(); |
| 89 params.Reset(); |
| 90 |
| 91 LocationSignature::Builder locations(zone, msig->return_count(), |
| 92 msig->parameter_count()); |
| 93 // Add return location(s). |
| 94 const int return_count = static_cast<int>(locations.return_count_); |
| 95 for (int i = 0; i < return_count; i++) { |
| 96 locations.AddReturn(rets.Next(msig->GetReturn(i))); |
| 97 } |
| 98 |
| 99 // Add register and/or stack parameter(s). |
| 100 const int parameter_count = static_cast<int>(msig->parameter_count()); |
| 101 for (int i = 0; i < parameter_count; i++) { |
| 102 locations.AddParam(params.Next(msig->GetParam(i))); |
| 103 } |
| 104 |
| 105 const RegList kCalleeSaveRegisters = 0; |
| 106 const RegList kCalleeSaveFPRegisters = 0; |
| 107 |
| 108 MachineType target_type = compiler::kMachAnyTagged; |
| 109 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); |
| 110 int stack_param_count = params.stack_offset; |
| 111 return new (zone) CallDescriptor( // -- |
| 112 CallDescriptor::kCallCodeObject, // kind |
| 113 target_type, // target MachineType |
| 114 target_loc, // target location |
| 115 msig, // machine_sig |
| 116 locations.Build(), // location_sig |
| 117 stack_param_count, // stack_parameter_count |
| 118 compiler::Operator::kNoProperties, // properties |
| 119 kCalleeSaveRegisters, // callee-saved registers |
| 120 kCalleeSaveFPRegisters, // callee-saved fp regs |
| 121 CallDescriptor::kNoFlags, // flags |
| 122 "c-call"); |
| 123 } |
| 124 |
| 125 private: |
| 126 Allocator& params; |
| 127 Allocator& rets; |
| 128 }; |
| 129 |
| 130 } // namespace |
| 131 |
| 132 |
| 133 static Handle<Code> CompileGraph(const char* name, CallDescriptor* desc, |
| 134 Graph* graph, Schedule* schedule = nullptr) { |
| 135 Isolate* isolate = CcTest::InitIsolateOnce(); |
| 136 Handle<Code> code = |
| 137 Pipeline::GenerateCodeForTesting(isolate, desc, graph, schedule); |
| 138 CHECK(!code.is_null()); |
| 139 #ifdef ENABLE_DISASSEMBLER |
| 140 if (FLAG_print_opt_code) { |
| 141 OFStream os(stdout); |
| 142 code->Disassemble(name, os); |
| 143 } |
| 144 #endif |
| 145 return code; |
| 146 } |
| 147 |
| 148 |
| 149 static Handle<Code> WrapWithCFunction(Handle<Code> inner, |
| 150 CallDescriptor* desc) { |
| 151 Zone zone; |
| 152 MachineSignature* msig = |
| 153 const_cast<MachineSignature*>(desc->GetMachineSignature()); |
| 154 int param_count = static_cast<int>(msig->parameter_count()); |
| 155 GraphAndBuilders caller(&zone); |
| 156 { |
| 157 GraphAndBuilders& b = caller; |
| 158 Node* start = b.graph()->NewNode(b.common()->Start(param_count + 3)); |
| 159 b.graph()->SetStart(start); |
| 160 Unique<HeapObject> unique = Unique<HeapObject>::CreateUninitialized(inner); |
| 161 Node* target = b.graph()->NewNode(b.common()->HeapConstant(unique)); |
| 162 |
| 163 // Add arguments to the call. |
| 164 Node** args = zone.NewArray<Node*>(param_count + 3); |
| 165 int index = 0; |
| 166 args[index++] = target; |
| 167 for (int i = 0; i < param_count; i++) { |
| 168 args[index] = b.graph()->NewNode(b.common()->Parameter(i), start); |
| 169 index++; |
| 170 } |
| 171 args[index++] = start; // effect. |
| 172 args[index++] = start; // control. |
| 173 |
| 174 // Build the call and return nodes. |
| 175 Node* call = |
| 176 b.graph()->NewNode(b.common()->Call(desc), param_count + 3, args); |
| 177 Node* ret = b.graph()->NewNode(b.common()->Return(), call, call, start); |
| 178 b.graph()->SetEnd(ret); |
| 179 } |
| 180 |
| 181 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, msig); |
| 182 |
| 183 return CompileGraph("wrapper", cdesc, caller.graph()); |
| 184 } |
| 185 |
| 186 |
| 187 static void TestInt32Sub(CallDescriptor* desc) { |
| 188 Isolate* isolate = CcTest::InitIsolateOnce(); |
| 189 HandleScope scope(isolate); |
| 190 Zone zone; |
| 191 GraphAndBuilders inner(&zone); |
| 192 { |
| 193 // Build the add function. |
| 194 GraphAndBuilders& b = inner; |
| 195 Node* start = b.graph()->NewNode(b.common()->Start(5)); |
| 196 b.graph()->SetStart(start); |
| 197 Node* p0 = b.graph()->NewNode(b.common()->Parameter(0), start); |
| 198 Node* p1 = b.graph()->NewNode(b.common()->Parameter(1), start); |
| 199 Node* add = b.graph()->NewNode(b.machine()->Int32Sub(), p0, p1); |
| 200 Node* ret = b.graph()->NewNode(b.common()->Return(), add, start, start); |
| 201 b.graph()->SetEnd(ret); |
| 202 } |
| 203 |
| 204 Handle<Code> inner_code = CompileGraph("Int32Sub", desc, inner.graph()); |
| 205 Handle<Code> wrapper = WrapWithCFunction(inner_code, desc); |
| 206 MachineSignature* msig = |
| 207 const_cast<MachineSignature*>(desc->GetMachineSignature()); |
| 208 CodeRunner<int32_t> runnable(isolate, wrapper, |
| 209 CSignature::FromMachine(&zone, msig)); |
| 210 |
| 211 FOR_INT32_INPUTS(i) { |
| 212 FOR_INT32_INPUTS(j) { |
| 213 int32_t expected = static_cast<int32_t>(static_cast<uint32_t>(*i) - |
| 214 static_cast<uint32_t>(*j)); |
| 215 int32_t result = runnable.Call(*i, *j); |
| 216 CHECK_EQ(expected, result); |
| 217 } |
| 218 } |
| 219 } |
| 220 |
| 221 |
| 222 static void CopyTwentyInt32(CallDescriptor* desc) { |
| 223 const int kNumParams = 20; |
| 224 int32_t input[kNumParams]; |
| 225 int32_t output[kNumParams]; |
| 226 Isolate* isolate = CcTest::InitIsolateOnce(); |
| 227 HandleScope scope(isolate); |
| 228 Handle<Code> inner = Handle<Code>::null(); |
| 229 { |
| 230 // Writes all parameters into the output buffer. |
| 231 Zone zone; |
| 232 Graph graph(&zone); |
| 233 RawMachineAssembler raw(isolate, &graph, desc); |
| 234 Node* base = raw.PointerConstant(output); |
| 235 for (int i = 0; i < kNumParams; i++) { |
| 236 Node* offset = raw.Int32Constant(i * sizeof(int32_t)); |
| 237 raw.Store(kMachInt32, base, offset, raw.Parameter(i)); |
| 238 } |
| 239 raw.Return(raw.Int32Constant(42)); |
| 240 inner = CompileGraph("CopyTwentyInt32", desc, &graph, raw.Export()); |
| 241 } |
| 242 |
| 243 CSignature0<int32_t> csig; |
| 244 Handle<Code> wrapper = Handle<Code>::null(); |
| 245 { |
| 246 // Loads parameters from the input buffer and calls the above code. |
| 247 Zone zone; |
| 248 Graph graph(&zone); |
| 249 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); |
| 250 RawMachineAssembler raw(isolate, &graph, cdesc); |
| 251 Node* base = raw.PointerConstant(input); |
| 252 Unique<HeapObject> unique = Unique<HeapObject>::CreateUninitialized(inner); |
| 253 Node* target = raw.HeapConstant(unique); |
| 254 Node** args = zone.NewArray<Node*>(kNumParams); |
| 255 for (int i = 0; i < kNumParams; i++) { |
| 256 Node* offset = raw.Int32Constant(i * sizeof(int32_t)); |
| 257 args[i] = raw.Load(kMachInt32, base, offset); |
| 258 } |
| 259 |
| 260 Node* call = raw.CallN(desc, target, args); |
| 261 raw.Return(call); |
| 262 wrapper = |
| 263 CompileGraph("CopyTwentyInt32-wrapper", cdesc, &graph, raw.Export()); |
| 264 } |
| 265 |
| 266 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); |
| 267 |
| 268 // Run the code, checking it correctly implements the memcpy. |
| 269 for (int i = 0; i < 5; i++) { |
| 270 uint32_t base = 1111111111u * i; |
| 271 for (int j = 0; j < kNumParams; j++) { |
| 272 input[j] = static_cast<int32_t>(base + 13 * j); |
| 273 } |
| 274 |
| 275 memset(output, 0, sizeof(output)); |
| 276 CHECK_EQ(42, runnable.Call()); |
| 277 |
| 278 for (int j = 0; j < kNumParams; j++) { |
| 279 CHECK_EQ(input[j], output[j]); |
| 280 } |
| 281 } |
| 282 } |
| 283 |
| 284 |
| 285 TEST(Run_Int32Sub_all_allocatable_pairs) { |
| 286 MachineType types[] = {kMachInt32, kMachInt32, kMachInt32}; |
| 287 MachineSignature sig(1, 2, types); |
| 288 for (int r = 0; r < Register::kMaxNumAllocatableRegisters; r++) { |
| 289 Zone zone; |
| 290 for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
| 291 for (int p1 = 0; p1 < Register::kMaxNumAllocatableRegisters; p1++) { |
| 292 if (p0 == p1) continue; |
| 293 int parray[] = {p0, p1}; |
| 294 int rarray[] = {r}; |
| 295 Allocator params(parray, 2, nullptr, 0); |
| 296 Allocator rets(rarray, 1, nullptr, 0); |
| 297 RegisterConfig config(params, rets); |
| 298 CallDescriptor* desc = config.Create(&zone, &sig); |
| 299 TestInt32Sub(desc); |
| 300 } |
| 301 } |
| 302 } |
| 303 } |
| 304 |
| 305 |
| 306 TEST(Run_Int32Sub_all_allocatable_single) { |
| 307 MachineType types[] = {kMachInt32, kMachInt32, kMachInt32}; |
| 308 MachineSignature sig(1, 2, types); |
| 309 for (int r = 0; r < Register::kMaxNumAllocatableRegisters; r++) { |
| 310 Zone zone; |
| 311 for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
| 312 int parray[] = {p0}; |
| 313 int rarray[] = {r}; |
| 314 Allocator params(parray, 1, nullptr, 0); |
| 315 Allocator rets(rarray, 1, nullptr, 0); |
| 316 RegisterConfig config(params, rets); |
| 317 CallDescriptor* desc = config.Create(&zone, &sig); |
| 318 TestInt32Sub(desc); |
| 319 } |
| 320 } |
| 321 } |
| 322 |
| 323 |
| 324 TEST(Run_CopyTwentyInt32_all_allocatable_pairs) { |
| 325 MachineType types[] = { |
| 326 kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
| 327 kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
| 328 kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
| 329 kMachInt32, kMachInt32, kMachInt32}; |
| 330 MachineSignature sig(1, 20, types); |
| 331 for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
| 332 Zone zone; |
| 333 for (int p1 = 0; p1 < Register::kMaxNumAllocatableRegisters; p1++) { |
| 334 if (p0 == p1) continue; |
| 335 int parray[] = {p0, p1}; |
| 336 int rarray[] = {0}; |
| 337 Allocator params(parray, 2, nullptr, 0); |
| 338 Allocator rets(rarray, 1, nullptr, 0); |
| 339 RegisterConfig config(params, rets); |
| 340 CallDescriptor* desc = config.Create(&zone, &sig); |
| 341 CopyTwentyInt32(desc); |
| 342 } |
| 343 } |
| 344 } |
| 345 |
| 346 |
| 347 #endif // V8_TURBOFAN_TARGET |
OLD | NEW |