summaryrefslogtreecommitdiffstats
path: root/chromium/v8/src/compiler/backend/instruction-codes.h
blob: 946745b62a4aa843e4dd70d72487bec54b7ea813 (plain)
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
102
103
104
105
106
107
108
109
110
111
112
113
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_COMPILER_BACKEND_INSTRUCTION_CODES_H_
#define V8_COMPILER_BACKEND_INSTRUCTION_CODES_H_

#include <iosfwd>

#if V8_TARGET_ARCH_ARM
#include "src/compiler/backend/arm/instruction-codes-arm.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/compiler/backend/arm64/instruction-codes-arm64.h"
#elif V8_TARGET_ARCH_IA32
#include "src/compiler/backend/ia32/instruction-codes-ia32.h"
#elif V8_TARGET_ARCH_MIPS64
#include "src/compiler/backend/mips64/instruction-codes-mips64.h"
#elif V8_TARGET_ARCH_LOONG64
#include "src/compiler/backend/loong64/instruction-codes-loong64.h"
#elif V8_TARGET_ARCH_X64
#include "src/compiler/backend/x64/instruction-codes-x64.h"
#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
#include "src/compiler/backend/ppc/instruction-codes-ppc.h"
#elif V8_TARGET_ARCH_S390
#include "src/compiler/backend/s390/instruction-codes-s390.h"
#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64
#include "src/compiler/backend/riscv/instruction-codes-riscv.h"
#else
#define TARGET_ARCH_OPCODE_LIST(V)
#define TARGET_ADDRESSING_MODE_LIST(V)
#endif
#include "src/base/bit-field.h"
#include "src/codegen/atomic-memory-order.h"
#include "src/compiler/write-barrier-kind.h"

namespace v8 {
namespace internal {
namespace compiler {

// Modes for ArchStoreWithWriteBarrier below.
enum class RecordWriteMode {
  kValueIsMap,
  kValueIsPointer,
  kValueIsIndirectPointer,
  kValueIsEphemeronKey,
  kValueIsAny,
};

inline RecordWriteMode WriteBarrierKindToRecordWriteMode(
    WriteBarrierKind write_barrier_kind) {
  switch (write_barrier_kind) {
    case kMapWriteBarrier:
      return RecordWriteMode::kValueIsMap;
    case kPointerWriteBarrier:
      return RecordWriteMode::kValueIsPointer;
    case kIndirectPointerWriteBarrier:
      return RecordWriteMode::kValueIsIndirectPointer;
    case kEphemeronKeyWriteBarrier:
      return RecordWriteMode::kValueIsEphemeronKey;
    case kFullWriteBarrier:
      return RecordWriteMode::kValueIsAny;
    case kNoWriteBarrier:
    // Should not be passed as argument.
    default:
      break;
  }
  UNREACHABLE();
}

#define COMMON_ARCH_OPCODE_WITH_MEMORY_ACCESS_MODE_LIST(V) \
  V(AtomicExchangeInt8)                                    \
  V(AtomicExchangeUint8)                                   \
  V(AtomicExchangeInt16)                                   \
  V(AtomicExchangeUint16)                                  \
  V(AtomicExchangeWord32)                                  \
  V(AtomicCompareExchangeInt8)                             \
  V(AtomicCompareExchangeUint8)                            \
  V(AtomicCompareExchangeInt16)                            \
  V(AtomicCompareExchangeUint16)                           \
  V(AtomicCompareExchangeWord32)                           \
  V(AtomicAddInt8)                                         \
  V(AtomicAddUint8)                                        \
  V(AtomicAddInt16)                                        \
  V(AtomicAddUint16)                                       \
  V(AtomicAddWord32)                                       \
  V(AtomicSubInt8)                                         \
  V(AtomicSubUint8)                                        \
  V(AtomicSubInt16)                                        \
  V(AtomicSubUint16)                                       \
  V(AtomicSubWord32)                                       \
  V(AtomicAndInt8)                                         \
  V(AtomicAndUint8)                                        \
  V(AtomicAndInt16)                                        \
  V(AtomicAndUint16)                                       \
  V(AtomicAndWord32)                                       \
  V(AtomicOrInt8)                                          \
  V(AtomicOrUint8)                                         \
  V(AtomicOrInt16)                                         \
  V(AtomicOrUint16)                                        \
  V(AtomicOrWord32)                                        \
  V(AtomicXorInt8)                                         \
  V(AtomicXorUint8)                                        \
  V(AtomicXorInt16)                                        \
  V(AtomicXorUint16)                                       \
  V(AtomicXorWord32)                                       \
  V(ArchStoreWithWriteBarrier)                             \
  V(ArchAtomicStoreWithWriteBarrier)                       \
  V(ArchStoreIndirectWithWriteBarrier)                     \
  V(AtomicLoadInt8)                                        \
  V(AtomicLoadUint8)                                       \
  V(AtomicLoadInt16)                                       \
  V(AtomicLoadUint16)                                      \
  V(AtomicLoadWord32)                                      \
  V(AtomicStoreWord8)                                      \
  V(AtomicStoreWord16)                                     \
  V(AtomicStoreWord32)

// Target-specific opcodes that specify which assembly sequence to emit.
// Most opcodes specify a single instruction.
#define COMMON_ARCH_OPCODE_LIST(V)                                         \
  /* Tail call opcodes are grouped together to make IsTailCall fast */     \
  /* and Arch call opcodes are grouped together to make */                 \
  /* IsCallWithDescriptorFlags fast */                                     \
  V(ArchTailCallCodeObject)                                                \
  V(ArchTailCallAddress)                                                   \
  IF_WASM(V, ArchTailCallWasm)                                             \
  /* Update IsTailCall if further TailCall opcodes are added */            \
                                                                           \
  V(ArchCallCodeObject)                                                    \
  V(ArchCallJSFunction)                                                    \
  IF_WASM(V, ArchCallWasmFunction)                                         \
  V(ArchCallBuiltinPointer)                                                \
  /* Update IsCallWithDescriptorFlags if further Call opcodes are added */ \
                                                                           \
  V(ArchPrepareCallCFunction)                                              \
  V(ArchSaveCallerRegisters)                                               \
  V(ArchRestoreCallerRegisters)                                            \
  V(ArchCallCFunction)                                                     \
  V(ArchPrepareTailCall)                                                   \
  V(ArchJmp)                                                               \
  V(ArchBinarySearchSwitch)                                                \
  V(ArchTableSwitch)                                                       \
  V(ArchNop)                                                               \
  V(ArchAbortCSADcheck)                                                    \
  V(ArchDebugBreak)                                                        \
  V(ArchComment)                                                           \
  V(ArchThrowTerminator)                                                   \
  V(ArchDeoptimize)                                                        \
  V(ArchRet)                                                               \
  V(ArchFramePointer)                                                      \
  V(ArchParentFramePointer)                                                \
  V(ArchTruncateDoubleToI)                                                 \
  V(ArchStackSlot)                                                         \
  V(ArchStackPointerGreaterThan)                                           \
  V(ArchStackCheckOffset)                                                  \
  V(Ieee754Float64Acos)                                                    \
  V(Ieee754Float64Acosh)                                                   \
  V(Ieee754Float64Asin)                                                    \
  V(Ieee754Float64Asinh)                                                   \
  V(Ieee754Float64Atan)                                                    \
  V(Ieee754Float64Atanh)                                                   \
  V(Ieee754Float64Atan2)                                                   \
  V(Ieee754Float64Cbrt)                                                    \
  V(Ieee754Float64Cos)                                                     \
  V(Ieee754Float64Cosh)                                                    \
  V(Ieee754Float64Exp)                                                     \
  V(Ieee754Float64Expm1)                                                   \
  V(Ieee754Float64Log)                                                     \
  V(Ieee754Float64Log1p)                                                   \
  V(Ieee754Float64Log10)                                                   \
  V(Ieee754Float64Log2)                                                    \
  V(Ieee754Float64Pow)                                                     \
  V(Ieee754Float64Sin)                                                     \
  V(Ieee754Float64Sinh)                                                    \
  V(Ieee754Float64Tan)                                                     \
  V(Ieee754Float64Tanh)                                                    \
  COMMON_ARCH_OPCODE_WITH_MEMORY_ACCESS_MODE_LIST(V)

#define ARCH_OPCODE_LIST(V)  \
  COMMON_ARCH_OPCODE_LIST(V) \
  TARGET_ARCH_OPCODE_LIST(V)

enum ArchOpcode {
#define DECLARE_ARCH_OPCODE(Name) k##Name,
  ARCH_OPCODE_LIST(DECLARE_ARCH_OPCODE)
#undef DECLARE_ARCH_OPCODE
#define COUNT_ARCH_OPCODE(Name) +1
      kLastArchOpcode = -1 ARCH_OPCODE_LIST(COUNT_ARCH_OPCODE)
#undef COUNT_ARCH_OPCODE
};

V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                           const ArchOpcode& ao);

// Addressing modes represent the "shape" of inputs to an instruction.
// Many instructions support multiple addressing modes. Addressing modes
// are encoded into the InstructionCode of the instruction and tell the
// code generator after register allocation which assembler method to call.
#define ADDRESSING_MODE_LIST(V) \
  V(None)                       \
  TARGET_ADDRESSING_MODE_LIST(V)

enum AddressingMode : uint8_t {
#define DECLARE_ADDRESSING_MODE(Name) kMode_##Name,
  ADDRESSING_MODE_LIST(DECLARE_ADDRESSING_MODE)
#undef DECLARE_ADDRESSING_MODE
#define COUNT_ADDRESSING_MODE(Name) +1
      kLastAddressingMode = -1 ADDRESSING_MODE_LIST(COUNT_ADDRESSING_MODE)
#undef COUNT_ADDRESSING_MODE
};

V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                           const AddressingMode& am);

// The mode of the flags continuation (see below).
enum FlagsMode {
  kFlags_none = 0,
  kFlags_branch = 1,
  kFlags_deoptimize = 2,
  kFlags_set = 3,
  kFlags_trap = 4,
  kFlags_select = 5,
};

V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                           const FlagsMode& fm);

// The condition of flags continuation (see below).
enum FlagsCondition {
  kEqual,
  kNotEqual,
  kSignedLessThan,
  kSignedGreaterThanOrEqual,
  kSignedLessThanOrEqual,
  kSignedGreaterThan,
  kUnsignedLessThan,
  kUnsignedGreaterThanOrEqual,
  kUnsignedLessThanOrEqual,
  kUnsignedGreaterThan,
  kFloatLessThanOrUnordered,
  kFloatGreaterThanOrEqual,
  kFloatLessThanOrEqual,
  kFloatGreaterThanOrUnordered,
  kFloatLessThan,
  kFloatGreaterThanOrEqualOrUnordered,
  kFloatLessThanOrEqualOrUnordered,
  kFloatGreaterThan,
  kUnorderedEqual,
  kUnorderedNotEqual,
  kOverflow,
  kNotOverflow,
  kPositiveOrZero,
  kNegative
};

static constexpr FlagsCondition kStackPointerGreaterThanCondition =
    kUnsignedGreaterThan;

inline FlagsCondition NegateFlagsCondition(FlagsCondition condition) {
  return static_cast<FlagsCondition>(condition ^ 1);
}

FlagsCondition CommuteFlagsCondition(FlagsCondition condition);

V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                           const FlagsCondition& fc);

enum MemoryAccessMode {
  kMemoryAccessDirect = 0,
  kMemoryAccessProtectedMemOutOfBounds = 1,
  kMemoryAccessProtectedNullDereference = 2,
};

enum class AtomicWidth { kWord32, kWord64 };

inline size_t AtomicWidthSize(AtomicWidth width) {
  switch (width) {
    case AtomicWidth::kWord32:
      return 4;
    case AtomicWidth::kWord64:
      return 8;
  }
  UNREACHABLE();
}

// The InstructionCode is an opaque, target-specific integer that encodes what
// code to emit for an instruction in the code generator. It is not interesting
// to the register allocator, as the inputs and flags on the instructions
// specify everything of interest.
using InstructionCode = uint32_t;

// Helpers for encoding / decoding InstructionCode into the fields needed
// for code generation. We encode the instruction, addressing mode, flags, and
// other information into a single InstructionCode which is stored as part of
// the instruction. Some fields in the layout of InstructionCode overlap as
// follows:
//                              ArchOpcodeField
//                              AddressingModeField
//                              FlagsModeField
//                              FlagsConditionField
// AtomicWidthField                 | RecordWriteModeField | LaneSizeField
// AtomicMemoryOrderField           |                      | VectorLengthField
// AtomicStoreRecordWriteModeField  |                      |
//                              AccessModeField
//
// or,
//
//                              ArchOpcodeField
//                              AddressingModeField
//                              FlagsModeField
//                              FlagsConditionField
// DeoptImmedArgsCountField    | ParamField   | MiscField
// DeoptFrameStateOffsetField  | FPParamField |
//
// Notably, AccessModeField can follow any of several sequences of fields.

using ArchOpcodeField = base::BitField<ArchOpcode, 0, 9>;
static_assert(ArchOpcodeField::is_valid(kLastArchOpcode),
              "All opcodes must fit in the 9-bit ArchOpcodeField.");
using AddressingModeField = ArchOpcodeField::Next<AddressingMode, 5>;
static_assert(
    AddressingModeField::is_valid(kLastAddressingMode),
    "All addressing modes must fit in the 5-bit AddressingModeField.");
using FlagsModeField = AddressingModeField::Next<FlagsMode, 3>;
using FlagsConditionField = FlagsModeField::Next<FlagsCondition, 5>;

// AtomicWidthField is used for the various Atomic opcodes. Only used on 64bit
// architectures. All atomic instructions on 32bit architectures are assumed to
// be 32bit wide.
using AtomicWidthField = FlagsConditionField::Next<AtomicWidth, 2>;
// AtomicMemoryOrderField is used for the various Atomic opcodes. This field is
// not used on all architectures. It is used on architectures where the codegen
// for kSeqCst and kAcqRel differ only by emitting fences.
using AtomicMemoryOrderField = AtomicWidthField::Next<AtomicMemoryOrder, 2>;
using AtomicStoreRecordWriteModeField =
    AtomicMemoryOrderField::Next<RecordWriteMode, 4>;

// Write modes for writes with barrier.
using RecordWriteModeField = FlagsConditionField::Next<RecordWriteMode, 3>;

// LaneSizeField and AccessModeField are helper types to encode/decode a lane
// size, an access mode, or both inside the overlapping MiscField.
#ifdef V8_TARGET_ARCH_X64
enum LaneSize { kL8 = 0, kL16 = 1, kL32 = 2, kL64 = 3 };
enum VectorLength { kV128 = 0, kV256 = 1, kV512 = 3 };
using LaneSizeField = FlagsConditionField::Next<LaneSize, 2>;
using VectorLengthField = LaneSizeField::Next<VectorLength, 2>;
#else
using LaneSizeField = FlagsConditionField::Next<int, 8>;
#endif  // V8_TARGET_ARCH_X64

// Denotes whether the instruction needs to emit an accompanying landing pad for
// the trap handler.
using AccessModeField =
    AtomicStoreRecordWriteModeField::Next<MemoryAccessMode, 2>;

// Since AccessModeField is defined in terms of atomics, this assert ensures it
// does not overlap with other fields it is used with.
static_assert(AtomicStoreRecordWriteModeField::kLastUsedBit >=
              RecordWriteModeField::kLastUsedBit);
#ifdef V8_TARGET_ARCH_X64
static_assert(AtomicStoreRecordWriteModeField::kLastUsedBit >=
              VectorLengthField::kLastUsedBit);
#else
static_assert(AtomicStoreRecordWriteModeField::kLastUsedBit >=
              LaneSizeField::kLastUsedBit);
#endif

// TODO(turbofan): {HasMemoryAccessMode} is currently only used to guard
// decoding (in CodeGenerator and InstructionScheduler). Encoding (in
// InstructionSelector) is not yet guarded. There are in fact instructions for
// which InstructionSelector does set a MemoryAccessMode but CodeGenerator
// doesn't care to consume it (e.g. kArm64LdrDecompressTaggedSigned). This is
// scary. {HasMemoryAccessMode} does not include these instructions, so they can
// be easily found by guarding encoding.
inline bool HasMemoryAccessMode(ArchOpcode opcode) {
#if defined(TARGET_ARCH_OPCODE_WITH_MEMORY_ACCESS_MODE_LIST)
  switch (opcode) {
#define CASE(Name) \
  case k##Name:    \
    return true;
    COMMON_ARCH_OPCODE_WITH_MEMORY_ACCESS_MODE_LIST(CASE)
    TARGET_ARCH_OPCODE_WITH_MEMORY_ACCESS_MODE_LIST(CASE)
#undef CASE
    default:
      return false;
  }
#else
  return false;
#endif
}

using DeoptImmedArgsCountField = FlagsConditionField::Next<int, 2>;
using DeoptFrameStateOffsetField = DeoptImmedArgsCountField::Next<int, 8>;

// ParamField and FPParamField represent the general purpose and floating point
// parameter counts of a direct call into C and are given 5 bits each, which
// allow storing a number up to the current maximum parameter count, which is 20
// (see kMaxCParameters defined in macro-assembler.h).
using ParamField = FlagsConditionField::Next<int, 5>;
using FPParamField = ParamField::Next<int, 5>;

// {MiscField} is used for a variety of things, depending on the opcode.
// TODO(turbofan): There should be an abstraction that ensures safe encoding and
// decoding. {HasMemoryAccessMode} and its uses are a small step in that
// direction.
using MiscField = FlagsConditionField::Next<int, 10>;

// This static assertion serves as an early warning if we are about to exhaust
// the available opcode space. If we are about to exhaust it, we should start
// looking into options to compress some opcodes (see
// https://crbug.com/v8/12093) before we fully run out of available opcodes.
// Otherwise we risk being unable to land an important security fix or merge
// back fixes that add new opcodes.
// It is OK to temporarily reduce the required slack if we have a tracking bug
// to reduce the number of used opcodes again.
static_assert(ArchOpcodeField::kMax - kLastArchOpcode >= 16,
              "We are running close to the number of available opcodes.");

}  // namespace compiler
}  // namespace internal
}  // namespace v8

#endif  // V8_COMPILER_BACKEND_INSTRUCTION_CODES_H_