assembler-s390.cc 29.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2014 the V8 project authors. All rights reserved.

37
#include "src/codegen/s390/assembler-s390.h"
38 39
#include <set>
#include <string>
40 41 42 43 44

#if V8_TARGET_ARCH_S390

#if V8_HOST_ARCH_S390
#include <elf.h>  // Required for auxv checks for STFLE support
45
#include <sys/auxv.h>
46 47 48 49
#endif

#include "src/base/bits.h"
#include "src/base/cpu.h"
50
#include "src/codegen/macro-assembler.h"
51
#include "src/codegen/s390/assembler-s390-inl.h"
52
#include "src/codegen/string-constants.h"
53
#include "src/deoptimizer/deoptimizer.h"
54 55 56 57 58 59 60 61 62 63

namespace v8 {
namespace internal {

// Get the CPU features enabled by the build.
static unsigned CpuFeaturesImpliedByCompiler() {
  unsigned answer = 0;
  return answer;
}

64
static bool supportsCPUFeature(const char* feature) {
65 66 67 68 69
  static std::set<std::string>& features = *new std::set<std::string>();
  static std::set<std::string>& all_available_features =
      *new std::set<std::string>({"iesan3", "zarch", "stfle", "msa", "ldisp",
                                  "eimm", "dfp", "etf3eh", "highgprs", "te",
                                  "vx"});
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  if (features.empty()) {
#if V8_HOST_ARCH_S390

#ifndef HWCAP_S390_VX
#define HWCAP_S390_VX 2048
#endif
#define CHECK_AVAILABILITY_FOR(mask, value) \
  if (f & mask) features.insert(value);

    // initialize feature vector
    uint64_t f = getauxval(AT_HWCAP);
    CHECK_AVAILABILITY_FOR(HWCAP_S390_ESAN3, "iesan3")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_ZARCH, "zarch")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_STFLE, "stfle")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_MSA, "msa")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_LDISP, "ldisp")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_EIMM, "eimm")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_DFP, "dfp")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_ETF3EH, "etf3eh")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_HIGH_GPRS, "highgprs")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_TE, "te")
    CHECK_AVAILABILITY_FOR(HWCAP_S390_VX, "vx")
#else
    // import all features
    features.insert(all_available_features.begin(),
                    all_available_features.end());
#endif
  }
  USE(all_available_features);
  return features.find(feature) != features.end();
}

102 103 104
#undef CHECK_AVAILABILITY_FOR
#undef HWCAP_S390_VX

105 106 107 108 109 110 111 112
// Check whether Store Facility STFLE instruction is available on the platform.
// Instruction returns a bit vector of the enabled hardware facilities.
static bool supportsSTFLE() {
#if V8_HOST_ARCH_S390
  static bool read_tried = false;
  static uint32_t auxv_hwcap = 0;

  if (!read_tried) {
113
    // Open the AUXV (auxiliary vector) pseudo-file
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    int fd = open("/proc/self/auxv", O_RDONLY);

    read_tried = true;
    if (fd != -1) {
#if V8_TARGET_ARCH_S390X
      static Elf64_auxv_t buffer[16];
      Elf64_auxv_t* auxv_element;
#else
      static Elf32_auxv_t buffer[16];
      Elf32_auxv_t* auxv_element;
#endif
      int bytes_read = 0;
      while (bytes_read >= 0) {
        // Read a chunk of the AUXV
        bytes_read = read(fd, buffer, sizeof(buffer));
        // Locate and read the platform field of AUXV if it is in the chunk
        for (auxv_element = buffer;
             auxv_element + sizeof(auxv_element) <= buffer + bytes_read &&
             auxv_element->a_type != AT_NULL;
             auxv_element++) {
          // We are looking for HWCAP entry in AUXV to search for STFLE support
          if (auxv_element->a_type == AT_HWCAP) {
            /* Note: Both auxv_hwcap and buffer are static */
            auxv_hwcap = auxv_element->a_un.a_val;
            goto done_reading;
          }
        }
      }
    done_reading:
      close(fd);
    }
  }

  // Did not find result
  if (0 == auxv_hwcap) {
    return false;
  }

  // HWCAP_S390_STFLE is defined to be 4 in include/asm/elf.h.  Currently
  // hardcoded in case that include file does not exist.
154 155
  const uint32_t _HWCAP_S390_STFLE = 4;
  return (auxv_hwcap & _HWCAP_S390_STFLE);
156 157 158 159 160 161
#else
  // STFLE is not available on non-s390 hosts
  return false;
#endif
}

162 163 164 165
bool CpuFeatures::SupportsWasmSimd128() {
  return CpuFeatures::IsSupported(VECTOR_ENHANCE_FACILITY_1);
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
void CpuFeatures::ProbeImpl(bool cross_compile) {
  supported_ |= CpuFeaturesImpliedByCompiler();
  icache_line_size_ = 256;

  // Only use statically determined features for cross compile (snapshot).
  if (cross_compile) return;

#ifdef DEBUG
  initialized_ = true;
#endif

  static bool performSTFLE = supportsSTFLE();

// Need to define host, as we are generating inlined S390 assembly to test
// for facilities.
#if V8_HOST_ARCH_S390
  if (performSTFLE) {
    // STFLE D(B) requires:
    //    GPR0 to specify # of double words to update minus 1.
    //      i.e. GPR0 = 0 for 1 doubleword
    //    D(B) to specify to memory location to store the facilities bits
    // The facilities we are checking for are:
    //   Bit 45 - Distinct Operands for instructions like ARK, SRK, etc.
    // As such, we require only 1 double word
190
    int64_t facilities[3] = {0L};
191
    int16_t reg0;
192 193 194 195
    // LHI sets up GPR0
    // STFLE is specified as .insn, as opcode is not recognized.
    // We register the instructions kill r0 (LHI) and the CC (STFLE).
    asm volatile(
196
        "lhi   %%r0,2\n"
197
        ".insn s,0xb2b00000,%0\n"
198
        : "=Q"(facilities), "=r"(reg0)
199 200 201
        :
        : "cc", "r0");

202
    uint64_t one = static_cast<uint64_t>(1);
203
    // Test for Distinct Operands Facility - Bit 45
204
    if (facilities[0] & (one << (63 - 45))) {
205 206 207
      supported_ |= (1u << DISTINCT_OPS);
    }
    // Test for General Instruction Extension Facility - Bit 34
208
    if (facilities[0] & (one << (63 - 34))) {
209 210 211
      supported_ |= (1u << GENERAL_INSTR_EXT);
    }
    // Test for Floating Point Extension Facility - Bit 37
212
    if (facilities[0] & (one << (63 - 37))) {
213 214
      supported_ |= (1u << FLOATING_POINT_EXT);
    }
215
    // Test for Vector Facility - Bit 129
216 217
    if (facilities[2] & (one << (63 - (129 - 128))) &&
        supportsCPUFeature("vx")) {
218 219
      supported_ |= (1u << VECTOR_FACILITY);
    }
220 221 222 223 224
    // Test for Vector Enhancement Facility 1 - Bit 135
    if (facilities[2] & (one << (63 - (135 - 128))) &&
        supportsCPUFeature("vx")) {
      supported_ |= (1u << VECTOR_ENHANCE_FACILITY_1);
    }
225 226 227 228 229
    // Test for Vector Enhancement Facility 2 - Bit 148
    if (facilities[2] & (one << (63 - (148 - 128))) &&
        supportsCPUFeature("vx")) {
      supported_ |= (1u << VECTOR_ENHANCE_FACILITY_2);
    }
jyan's avatar
jyan committed
230 231 232 233
    // Test for Miscellaneous Instruction Extension Facility - Bit 58
    if (facilities[0] & (1lu << (63 - 58))) {
      supported_ |= (1u << MISC_INSTR_EXT2);
    }
234 235 236 237 238 239 240
  }
#else
  // All distinct ops instructions can be simulated
  supported_ |= (1u << DISTINCT_OPS);
  // RISBG can be simulated
  supported_ |= (1u << GENERAL_INSTR_EXT);
  supported_ |= (1u << FLOATING_POINT_EXT);
jyan's avatar
jyan committed
241
  supported_ |= (1u << MISC_INSTR_EXT2);
242
  USE(performSTFLE);  // To avoid assert
243
  USE(supportsCPUFeature);
244
  supported_ |= (1u << VECTOR_FACILITY);
245
  supported_ |= (1u << VECTOR_ENHANCE_FACILITY_1);
246 247
#endif
  supported_ |= (1u << FPU);
248 249 250 251 252 253

  // Set a static value on whether Simd is supported.
  // This variable is only used for certain archs to query SupportWasmSimd128()
  // at runtime in builtins using an extern ref. Other callers should use
  // CpuFeatures::SupportWasmSimd128().
  CpuFeatures::supports_wasm_simd_128_ = CpuFeatures::SupportsWasmSimd128();
254 255 256
}

void CpuFeatures::PrintTarget() {
257
  const char* s390_arch = nullptr;
258 259 260 261 262 263 264

#if V8_TARGET_ARCH_S390X
  s390_arch = "s390x";
#else
  s390_arch = "s390";
#endif

265
  PrintF("target %s\n", s390_arch);
266 267 268
}

void CpuFeatures::PrintFeatures() {
269 270 271 272 273
  PrintF("FPU=%d\n", CpuFeatures::IsSupported(FPU));
  PrintF("FPU_EXT=%d\n", CpuFeatures::IsSupported(FLOATING_POINT_EXT));
  PrintF("GENERAL_INSTR=%d\n", CpuFeatures::IsSupported(GENERAL_INSTR_EXT));
  PrintF("DISTINCT_OPS=%d\n", CpuFeatures::IsSupported(DISTINCT_OPS));
  PrintF("VECTOR_FACILITY=%d\n", CpuFeatures::IsSupported(VECTOR_FACILITY));
274 275 276 277
  PrintF("VECTOR_ENHANCE_FACILITY_1=%d\n",
         CpuFeatures::IsSupported(VECTOR_ENHANCE_FACILITY_1));
  PrintF("VECTOR_ENHANCE_FACILITY_2=%d\n",
         CpuFeatures::IsSupported(VECTOR_ENHANCE_FACILITY_2));
278
  PrintF("MISC_INSTR_EXT2=%d\n", CpuFeatures::IsSupported(MISC_INSTR_EXT2));
279 280 281 282 283 284 285 286 287 288 289 290 291
}

Register ToRegister(int num) {
  DCHECK(num >= 0 && num < kNumRegisters);
  const Register kRegisters[] = {r0, r1, r2,  r3, r4, r5,  r6,  r7,
                                 r8, r9, r10, fp, ip, r13, r14, sp};
  return kRegisters[num];
}

// -----------------------------------------------------------------------------
// Implementation of RelocInfo

const int RelocInfo::kApplyMask =
292 293
    RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
    RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE);
294 295 296 297 298 299 300 301 302 303 304

bool RelocInfo::IsCodedSpecially() {
  // The deserializer needs to know whether a pointer is specially
  // coded.  Being specially coded on S390 means that it is an iihf/iilf
  // instruction sequence, and that is always the case inside code
  // objects.
  return true;
}

bool RelocInfo::IsInConstantPool() { return false; }

305 306
uint32_t RelocInfo::wasm_call_tag() const {
  DCHECK(rmode_ == WASM_CALL || rmode_ == WASM_STUB_CALL);
307 308 309 310
  return static_cast<uint32_t>(
      Assembler::target_address_at(pc_, constant_pool_));
}

311 312 313 314
// -----------------------------------------------------------------------------
// Implementation of Operand and MemOperand
// See assembler-s390-inl.h for inlined constructors

315 316
Operand::Operand(Handle<HeapObject> handle) {
  AllowHandleDereference using_location;
317
  rm_ = no_reg;
318
  value_.immediate = static_cast<intptr_t>(handle.address());
319
  rmode_ = RelocInfo::FULL_EMBEDDED_OBJECT;
320 321
}

322 323 324
Operand Operand::EmbeddedNumber(double value) {
  int32_t smi;
  if (DoubleToSmiInteger(value, &smi)) return Operand(Smi::FromInt(smi));
325
  Operand result(0, RelocInfo::FULL_EMBEDDED_OBJECT);
326 327 328 329 330
  result.is_heap_object_request_ = true;
  result.value_.heap_object_request = HeapObjectRequest(value);
  return result;
}

331
Operand Operand::EmbeddedStringConstant(const StringConstantBase* str) {
332
  Operand result(0, RelocInfo::FULL_EMBEDDED_OBJECT);
333 334 335 336 337
  result.is_heap_object_request_ = true;
  result.value_.heap_object_request = HeapObjectRequest(str);
  return result;
}

338 339
MemOperand::MemOperand(Register rn, int32_t offset)
    : baseRegister(rn), indexRegister(r0), offset_(offset) {}
340

341 342
MemOperand::MemOperand(Register rx, Register rb, int32_t offset)
    : baseRegister(rb), indexRegister(rx), offset_(offset) {}
343

344
void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
345
  DCHECK_IMPLIES(isolate == nullptr, heap_object_requests_.empty());
346 347
  for (auto& request : heap_object_requests_) {
    Handle<HeapObject> object;
348
    Address pc = reinterpret_cast<Address>(buffer_start_) + request.offset();
349
    switch (request.kind()) {
350
      case HeapObjectRequest::kHeapNumber: {
351 352
        object = isolate->factory()->NewHeapNumber<AllocationType::kOld>(
            request.heap_number());
353
        set_target_address_at(pc, kNullAddress, object.address(),
354 355
                              SKIP_ICACHE_FLUSH);
        break;
356 357 358 359 360 361 362 363
      }
      case HeapObjectRequest::kStringConstant: {
        const StringConstantBase* str = request.string();
        CHECK_NOT_NULL(str);
        set_target_address_at(pc, kNullAddress,
                              str->AllocateStringConstant(isolate).address());
        break;
      }
364 365 366 367
    }
  }
}

368 369 370
// -----------------------------------------------------------------------------
// Specific instructions, constants, and masks.

371 372
Assembler::Assembler(const AssemblerOptions& options,
                     std::unique_ptr<AssemblerBuffer> buffer)
373 374
    : AssemblerBase(options, std::move(buffer)),
      scratch_register_list_(ip.bit()) {
375
  reloc_info_writer.Reposition(buffer_start_ + buffer_->size(), pc_);
376 377 378 379
  last_bound_pos_ = 0;
  relocations_.reserve(128);
}

380 381 382
void Assembler::GetCode(Isolate* isolate, CodeDesc* desc,
                        SafepointTableBuilder* safepoint_table_builder,
                        int handler_table_offset) {
383 384 385 386 387 388 389 390 391
  // As a crutch to avoid having to add manual Align calls wherever we use a
  // raw workflow to create Code objects (mostly in tests), add another Align
  // call here. It does no harm - the end of the Code object is aligned to the
  // (larger) kCodeAlignment anyways.
  // TODO(jgruber): Consider moving responsibility for proper alignment to
  // metadata table builders (safepoint, handler, constant pool, code
  // comments).
  DataAlign(Code::kMetadataAlignment);

392 393
  EmitRelocations();

394 395
  int code_comments_size = WriteCodeComments();

396 397
  AllocateAndInstallRequestedHeapObjects(isolate);

398
  // Set up code descriptor.
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
  // TODO(jgruber): Reconsider how these offsets and sizes are maintained up to
  // this point to make CodeDesc initialization less fiddly.

  static constexpr int kConstantPoolSize = 0;
  const int instruction_size = pc_offset();
  const int code_comments_offset = instruction_size - code_comments_size;
  const int constant_pool_offset = code_comments_offset - kConstantPoolSize;
  const int handler_table_offset2 = (handler_table_offset == kNoHandlerTable)
                                        ? constant_pool_offset
                                        : handler_table_offset;
  const int safepoint_table_offset =
      (safepoint_table_builder == kNoSafepointTable)
          ? handler_table_offset2
          : safepoint_table_builder->GetCodeOffset();
  const int reloc_info_offset =
      static_cast<int>(reloc_info_writer.pos() - buffer_->start());
  CodeDesc::Initialize(desc, this, safepoint_table_offset,
                       handler_table_offset2, constant_pool_offset,
                       code_comments_offset, reloc_info_offset);
418 419 420
}

void Assembler::Align(int m) {
421
  DCHECK(m >= 4 && base::bits::IsPowerOfTwo(m));
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
  while ((pc_offset() & (m - 1)) != 0) {
    nop(0);
  }
}

void Assembler::CodeTargetAlign() { Align(8); }

Condition Assembler::GetCondition(Instr instr) {
  switch (instr & kCondMask) {
    case BT:
      return eq;
    case BF:
      return ne;
    default:
      UNIMPLEMENTED();
  }
  return al;
}

#if V8_TARGET_ARCH_S390X
// This code assumes a FIXED_SEQUENCE for 64bit loads (iihf/iilf)
bool Assembler::Is64BitLoadIntoIP(SixByteInstr instr1, SixByteInstr instr2) {
  // Check the instructions are the iihf/iilf load into ip
  return (((instr1 >> 32) == 0xC0C8) && ((instr2 >> 32) == 0xC0C9));
}
#else
// This code assumes a FIXED_SEQUENCE for 32bit loads (iilf)
bool Assembler::Is32BitLoadIntoIP(SixByteInstr instr) {
  // Check the instruction is an iilf load into ip/r12.
  return ((instr >> 32) == 0xC0C9);
}
#endif

// Labels refer to positions in the (to be) generated code.
// There are bound, linked, and unused labels.
//
// Bound labels refer to known positions in the already
// generated code. pos() is the position the label refers to.
//
// Linked labels refer to unknown positions in the code
// to be generated; pos() is the position of the last
// instruction using the label.

// The link chain is terminated by a negative code position (must be aligned)
const int kEndOfChain = -4;

// Returns the target address of the relative instructions, typically
// of the form: pos + imm (where immediate is in # of halfwords for
// BR* and LARL).
int Assembler::target_at(int pos) {
  SixByteInstr instr = instr_at(pos);
  // check which type of branch this is 16 or 26 bit offset
474
  Opcode opcode = Instruction::S390OpcodeValue(buffer_start_ + pos);
475

476
  if (BRC == opcode || BRCT == opcode || BRCTG == opcode || BRXH == opcode) {
477
    int16_t imm16 = SIGN_EXT_IMM16((instr & kImm16Mask));
478
    imm16 <<= 1;  // immediate is in # of halfwords
479 480 481 482 483
    if (imm16 == 0) return kEndOfChain;
    return pos + imm16;
  } else if (LLILF == opcode || BRCL == opcode || LARL == opcode ||
             BRASL == opcode) {
    int32_t imm32 =
484
        static_cast<int32_t>(instr & (static_cast<uint64_t>(0xFFFFFFFF)));
485 486 487 488
    if (LLILF != opcode)
      imm32 <<= 1;  // BR* + LARL treat immediate in # of halfwords
    if (imm32 == 0) return kEndOfChain;
    return pos + imm32;
489 490 491 492 493 494 495
  } else if (BRXHG == opcode) {
    // offset is in bits 16-31 of 48 bit instruction
    instr = instr >> 16;
    int16_t imm16 = SIGN_EXT_IMM16((instr & kImm16Mask));
    imm16 <<= 1;  // immediate is in # of halfwords
    if (imm16 == 0) return kEndOfChain;
    return pos + imm16;
496 497 498 499 500 501 502 503 504 505
  }

  // Unknown condition
  DCHECK(false);
  return -1;
}

// Update the target address of the current relative instruction.
void Assembler::target_at_put(int pos, int target_pos, bool* is_branch) {
  SixByteInstr instr = instr_at(pos);
506
  Opcode opcode = Instruction::S390OpcodeValue(buffer_start_ + pos);
507 508

  if (is_branch != nullptr) {
509 510 511
    *is_branch =
        (opcode == BRC || opcode == BRCT || opcode == BRCTG || opcode == BRCL ||
         opcode == BRASL || opcode == BRXH || opcode == BRXHG);
512 513
  }

514
  if (BRC == opcode || BRCT == opcode || BRCTG == opcode || BRXH == opcode) {
515
    int16_t imm16 = target_pos - pos;
516
    instr &= (~0xFFFF);
517
    DCHECK(is_int16(imm16));
518 519 520 521 522
    instr_at_put<FourByteInstr>(pos, instr | (imm16 >> 1));
    return;
  } else if (BRCL == opcode || LARL == opcode || BRASL == opcode) {
    // Immediate is in # of halfwords
    int32_t imm32 = target_pos - pos;
523
    instr &= (~static_cast<uint64_t>(0xFFFFFFFF));
524 525 526
    instr_at_put<SixByteInstr>(pos, instr | (imm32 >> 1));
    return;
  } else if (LLILF == opcode) {
527
    DCHECK(target_pos == kEndOfChain || target_pos >= 0);
528
    // Emitted label constant, not part of a branch.
529
    // Make label relative to Code pointer of generated Code object.
530
    int32_t imm32 = target_pos + (Code::kHeaderSize - kHeapObjectTag);
531
    instr &= (~static_cast<uint64_t>(0xFFFFFFFF));
532 533
    instr_at_put<SixByteInstr>(pos, instr | imm32);
    return;
534 535 536
  } else if (BRXHG == opcode) {
    // Immediate is in bits 16-31 of 48 bit instruction
    int32_t imm16 = target_pos - pos;
537 538
    instr &= (0xFFFF0000FFFF);  // clear bits 16-31
    imm16 &= 0xFFFF;            // clear high halfword
539 540 541 542
    imm16 <<= 16;
    // Immediate is in # of halfwords
    instr_at_put<SixByteInstr>(pos, instr | (imm16 >> 1));
    return;
543 544 545 546 547 548
  }
  DCHECK(false);
}

// Returns the maximum number of bits given instruction can address.
int Assembler::max_reach_from(int pos) {
549
  Opcode opcode = Instruction::S390OpcodeValue(buffer_start_ + pos);
550 551
  // Check which type of instr.  In theory, we can return
  // the values below + 1, given offset is # of halfwords
552
  if (BRC == opcode || BRCT == opcode || BRCTG == opcode || BRXH == opcode ||
553
      BRXHG == opcode) {
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
    return 16;
  } else if (LLILF == opcode || BRCL == opcode || LARL == opcode ||
             BRASL == opcode) {
    return 31;  // Using 31 as workaround instead of 32 as
                // is_intn(x,32) doesn't work on 32-bit platforms.
                // llilf: Emitted label constant, not part of
                //        a branch (regexp PushBacktrack).
  }
  DCHECK(false);
  return 16;
}

void Assembler::bind_to(Label* L, int pos) {
  DCHECK(0 <= pos && pos <= pc_offset());  // must have a valid binding position
  bool is_branch = false;
  while (L->is_linked()) {
    int fixup_pos = L->pos();
#ifdef DEBUG
    int32_t offset = pos - fixup_pos;
    int maxReach = max_reach_from(fixup_pos);
#endif
    next(L);  // call next before overwriting link with target at fixup_pos
    DCHECK(is_intn(offset, maxReach));
    target_at_put(fixup_pos, pos, &is_branch);
  }
  L->bind_to(pos);

  // Keep track of the last bound label so we don't eliminate any instructions
  // before a bound label.
  if (pos > last_bound_pos_) last_bound_pos_ = pos;
}

void Assembler::bind(Label* L) {
  DCHECK(!L->is_bound());  // label can only be bound once
  bind_to(L, pc_offset());
}

void Assembler::next(Label* L) {
  DCHECK(L->is_linked());
  int link = target_at(L->pos());
  if (link == kEndOfChain) {
    L->Unuse();
  } else {
597
    DCHECK_GE(link, 0);
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    L->link_to(link);
  }
}

int Assembler::link(Label* L) {
  int position;
  if (L->is_bound()) {
    position = L->pos();
  } else {
    if (L->is_linked()) {
      position = L->pos();  // L's link
    } else {
      // was: target_pos = kEndOfChain;
      // However, using self to mark the first reference
      // should avoid most instances of branch offset overflow.  See
      // target_at() for where this is converted back to kEndOfChain.
      position = pc_offset();
    }
    L->link_to(pc_offset());
  }

  return position;
}

void Assembler::load_label_offset(Register r1, Label* L) {
  int target_pos;
  int constant;
  if (L->is_bound()) {
    target_pos = L->pos();
    constant = target_pos + (Code::kHeaderSize - kHeapObjectTag);
  } else {
    if (L->is_linked()) {
      target_pos = L->pos();  // L's link
    } else {
      // was: target_pos = kEndOfChain;
      // However, using branch to self to mark the first reference
      // should avoid most instances of branch offset overflow.  See
      // target_at() for where this is converted back to kEndOfChain.
      target_pos = pc_offset();
    }
    L->link_to(pc_offset());

    constant = target_pos - pc_offset();
  }
  llilf(r1, Operand(constant));
}

// Pseudo op - branch on condition
646 647
void Assembler::branchOnCond(Condition c, int branch_offset, bool is_bound,
                             bool force_long_branch) {
648
  int offset_in_halfwords = branch_offset / 2;
649
  if (is_bound && is_int16(offset_in_halfwords) && !force_long_branch) {
jyan's avatar
jyan committed
650
    brc(c, Operand(offset_in_halfwords));  // short jump
651
  } else {
652
    brcl(c, Operand(offset_in_halfwords));  // long jump
653 654 655 656 657 658
  }
}

// Exception-generating instructions and debugging support.
// Stops with a non-negative code less than kNumOfWatchedStops support
// enabling/disabling and a counter feature. See simulator-s390.h .
659
void Assembler::stop(Condition cond, int32_t code, CRegister cr) {
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
  if (cond != al) {
    Label skip;
    b(NegateCondition(cond), &skip, Label::kNear);
    bkpt(0);
    bind(&skip);
  } else {
    bkpt(0);
  }
}

void Assembler::bkpt(uint32_t imm16) {
  // GDB software breakpoint instruction
  emit2bytes(0x0001);
}

// Pseudo instructions.
void Assembler::nop(int type) {
  switch (type) {
    case 0:
      lr(r0, r0);
      break;
    case DEBUG_BREAK_NOP:
      // TODO(john.yan): Use a better NOP break
      oill(r3, Operand::Zero());
      break;
    default:
      UNIMPLEMENTED();
  }
}

// -------------------------
// Load Address Instructions
// -------------------------
// Load Address Relative Long
void Assembler::larl(Register r1, Label* l) {
  larl(r1, Operand(branch_offset(l)));
}

void Assembler::EnsureSpaceFor(int space_needed) {
  if (buffer_space() <= (kGap + space_needed)) {
    GrowBuffer(space_needed);
  }
}

704
void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
705
  DCHECK(RelocInfo::IsCodeTarget(rmode));
706 707
  EnsureSpace ensure_space(this);

708 709
  RecordRelocInfo(rmode);
  int32_t target_index = AddCodeTarget(target);
710 711 712 713 714
  brasl(r14, Operand(target_index));
}

void Assembler::jump(Handle<Code> target, RelocInfo::Mode rmode,
                     Condition cond) {
715
  DCHECK(RelocInfo::IsRelativeCodeTarget(rmode));
716 717
  EnsureSpace ensure_space(this);

718 719
  RecordRelocInfo(rmode);
  int32_t target_index = AddCodeTarget(target);
720
  brcl(cond, Operand(target_index));
721 722 723 724 725 726 727
}

// end of S390instructions

bool Assembler::IsNop(SixByteInstr instr, int type) {
  DCHECK((0 == type) || (DEBUG_BREAK_NOP == type));
  if (DEBUG_BREAK_NOP == type) {
728
    return ((instr & 0xFFFFFFFF) == 0xA53B0000);  // oill r3, 0
729
  }
730
  return ((instr & 0xFFFF) == 0x1800);  // lr r0,r0
731 732
}

733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
// dummy instruction reserved for special use.
void Assembler::dumy(int r1, int x2, int b2, int d2) {
#if defined(USE_SIMULATOR)
  int op = 0xE353;
  uint64_t code = (static_cast<uint64_t>(op & 0xFF00)) * B32 |
                  (static_cast<uint64_t>(r1) & 0xF) * B36 |
                  (static_cast<uint64_t>(x2) & 0xF) * B32 |
                  (static_cast<uint64_t>(b2) & 0xF) * B28 |
                  (static_cast<uint64_t>(d2 & 0x0FFF)) * B16 |
                  (static_cast<uint64_t>(d2 & 0x0FF000)) >> 4 |
                  (static_cast<uint64_t>(op & 0x00FF));
  emit6bytes(code);
#endif
}

748
void Assembler::GrowBuffer(int needed) {
749
  DCHECK_EQ(buffer_start_, buffer_->start());
750 751

  // Compute new buffer size.
752 753 754 755
  int old_size = buffer_->size();
  int new_size = std::min(2 * old_size, old_size + 1 * MB);
  int space = buffer_space() + (new_size - old_size);
  new_size += (space < needed) ? needed - space : 0;
756 757 758

  // Some internal data structures overflow for very large buffers,
  // they must ensure that kMaximalBufferSize is not too large.
759
  if (new_size > kMaximalBufferSize) {
760
    V8::FatalProcessOutOfMemory(nullptr, "Assembler::GrowBuffer");
761
  }
762 763

  // Set up new buffer.
764 765 766
  std::unique_ptr<AssemblerBuffer> new_buffer = buffer_->Grow(new_size);
  DCHECK_EQ(new_size, new_buffer->size());
  byte* new_start = new_buffer->start();
767 768

  // Copy the data.
769 770 771 772 773 774
  intptr_t pc_delta = new_start - buffer_start_;
  intptr_t rc_delta = (new_start + new_size) - (buffer_start_ + old_size);
  size_t reloc_size = (buffer_start_ + old_size) - reloc_info_writer.pos();
  MemMove(new_start, buffer_start_, pc_offset());
  MemMove(reloc_info_writer.pos() + rc_delta, reloc_info_writer.pos(),
          reloc_size);
775 776

  // Switch buffers.
777 778
  buffer_ = std::move(new_buffer);
  buffer_start_ = new_start;
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
  pc_ += pc_delta;
  reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
                               reloc_info_writer.last_pc() + pc_delta);

  // None of our relocation types are pc relative pointing outside the code
  // buffer nor pc absolute pointing inside the code buffer, so there is no need
  // to relocate any emitted relocation entries.
}

void Assembler::db(uint8_t data) {
  CheckBuffer();
  *reinterpret_cast<uint8_t*>(pc_) = data;
  pc_ += sizeof(uint8_t);
}

794
void Assembler::dd(uint32_t data, RelocInfo::Mode rmode) {
795
  CheckBuffer();
796 797 798 799
  if (!RelocInfo::IsNone(rmode)) {
    DCHECK(RelocInfo::IsDataEmbeddedObject(rmode));
    RecordRelocInfo(rmode);
  }
800 801 802 803
  *reinterpret_cast<uint32_t*>(pc_) = data;
  pc_ += sizeof(uint32_t);
}

804
void Assembler::dq(uint64_t value, RelocInfo::Mode rmode) {
805
  CheckBuffer();
806 807 808 809
  if (!RelocInfo::IsNone(rmode)) {
    DCHECK(RelocInfo::IsDataEmbeddedObject(rmode));
    RecordRelocInfo(rmode);
  }
810 811 812 813
  *reinterpret_cast<uint64_t*>(pc_) = value;
  pc_ += sizeof(uint64_t);
}

814
void Assembler::dp(uintptr_t data, RelocInfo::Mode rmode) {
815
  CheckBuffer();
816 817 818 819
  if (!RelocInfo::IsNone(rmode)) {
    DCHECK(RelocInfo::IsDataEmbeddedObject(rmode));
    RecordRelocInfo(rmode);
  }
820 821 822 823 824
  *reinterpret_cast<uintptr_t*>(pc_) = data;
  pc_ += sizeof(uintptr_t);
}

void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
825
  if (!ShouldRecordRelocInfo(rmode)) return;
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
  DeferredRelocInfo rinfo(pc_offset(), rmode, data);
  relocations_.push_back(rinfo);
}

void Assembler::emit_label_addr(Label* label) {
  CheckBuffer();
  RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
  int position = link(label);
  DCHECK(label->is_bound());
  // Keep internal references relative until EmitRelocations.
  dp(position);
}

void Assembler::EmitRelocations() {
  EnsureSpaceFor(relocations_.size() * kMaxRelocSize);

  for (std::vector<DeferredRelocInfo>::iterator it = relocations_.begin();
       it != relocations_.end(); it++) {
    RelocInfo::Mode rmode = it->rmode();
845
    Address pc = reinterpret_cast<Address>(buffer_start_) + it->position();
846
    RelocInfo rinfo(pc, rmode, it->data(), Code());
847 848 849 850

    // Fix up internal references now that they are guaranteed to be bound.
    if (RelocInfo::IsInternalReference(rmode)) {
      // Jump table entry
851
      Address pos = Memory<Address>(pc);
852
      Memory<Address>(pc) = reinterpret_cast<Address>(buffer_start_) + pos;
853 854
    } else if (RelocInfo::IsInternalReferenceEncoded(rmode)) {
      // mov sequence
855
      Address pos = target_address_at(pc, 0);
856 857
      set_target_address_at(pc, 0,
                            reinterpret_cast<Address>(buffer_start_) + pos,
858
                            SKIP_ICACHE_FLUSH);
859 860 861 862 863 864
    }

    reloc_info_writer.Write(&rinfo);
  }
}

865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler)
    : assembler_(assembler),
      old_available_(*assembler->GetScratchRegisterList()) {}

UseScratchRegisterScope::~UseScratchRegisterScope() {
  *assembler_->GetScratchRegisterList() = old_available_;
}

Register UseScratchRegisterScope::Acquire() {
  RegList* available = assembler_->GetScratchRegisterList();
  DCHECK_NOT_NULL(available);
  DCHECK_NE(*available, 0);
  int index = static_cast<int>(base::bits::CountTrailingZeros32(*available));
  Register reg = Register::from_code(index);
  *available &= ~reg.bit();
  return reg;
}
882 883 884
}  // namespace internal
}  // namespace v8
#endif  // V8_TARGET_ARCH_S390