Skip to content

Commit a4c50f0

Browse files
jiepan-intelv8-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[APX] [assembler] Add emit_rex2_or_rex prefix wrapper functions
APX places r16-r31 out of reach of the legacy REX prefix: an instruction referencing one of them, directly or as the base or index of a memory operand, has to be encoded with REX2 instead. Add emit_rex2_or_rex, emit_rex2_or_rex_64, emit_rex2_or_rex_32 and emit_optional_rex2_or_rex_32, mirroring the existing emit_rex_* family, which pick REX2 when an operand needs it and fall back to the legacy prefix otherwise. REX2 also carries the opcode map in its M0 bit and must be the last byte before the opcode, so the 0x0F escape byte has to be dropped exactly when a REX2 prefix is emitted. Rather than report that back to the call site, the wrappers take the map as a Rex2MapID and emit the escape byte themselves, so the prefix and the escape cannot get out of sync. REX2 has a single map bit, so instructions in the 0x0F38 and 0x0F3A maps still need a VEX or EVEX encoding to reach the extended GPRs. Bug: 474204750 Change-Id: I102a423e19dc8c1374265248325951caf1ab3ea6 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8422347 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Pan, Jie <jie.pan@intel.com> Cr-Commit-Position: refs/heads/main@{#109955}
1 parent 39ee186 commit a4c50f0

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

src/codegen/x64/assembler-x64.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3213,6 +3213,164 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
32133213
}
32143214
}
32153215

3216+
#ifdef V8_ENABLE_APX_F
3217+
// True if the operand can only be encoded with a REX2 prefix, i.e. if it
3218+
// references one of the extended GPRs r16-r31 -- directly, or as the base or
3219+
// index register of a memory operand.
3220+
inline bool needs_rex2_prefix(Register reg) { return reg.bit4(); }
3221+
inline bool needs_rex2_prefix(Operand op) { return op.rex2(); }
3222+
3223+
template <class P1, class P2>
3224+
bool needs_rex2_prefix(P1 p1, P2 p2) {
3225+
return needs_rex2_prefix(p1) || needs_rex2_prefix(p2);
3226+
}
3227+
#endif // V8_ENABLE_APX_F
3228+
3229+
// Emits the 0x0F escape byte if the opcode lives in legacy map 1. Only a
3230+
// legacy REX prefix needs this: a REX2 prefix carries the map in its M0 bit
3231+
// and must be the last byte before the opcode.
3232+
inline void emit_legacy_map_escape(Rex2MapID m) {
3233+
if (m == kRex2Map1) emit(0x0F);
3234+
}
3235+
3236+
// emit_rex2_or_rex[_64|_32](operands..., m) emits everything between the
3237+
// legacy prefixes and the opcode of an instruction whose opcode lives in
3238+
// legacy map {m} -- kRex2Map0 for a one-byte opcode, kRex2Map1 for a
3239+
// 0x0F-escaped one:
3240+
// - a single REX2 prefix, if any operand references r16-r31. REX2 encodes
3241+
// the map itself, so it replaces the 0x0F escape byte.
3242+
// - otherwise the legacy REX prefix, followed by the 0x0F escape byte if
3243+
// {m} is kRex2Map1.
3244+
// The caller emits only the opcode, so the prefix and the escape byte cannot
3245+
// get out of sync. Any legacy prefix (0x66/0xF2/0xF3/segment override) must
3246+
// already have been emitted; REX and REX2 both come last.
3247+
//
3248+
// REX2 has a single map bit, so instructions in the 0x0F38 and 0x0F3A maps
3249+
// cannot use these helpers at all -- they need a VEX or EVEX encoding to
3250+
// reach the extended GPRs.
3251+
//
3252+
// Unlike the emit_rex_* helpers these do not accept an XMMRegister yet: the
3253+
// underlying emit_rex2_* emitters only take a GPR in the reg field.
3254+
void emit_rex2_or_rex(int size, Rex2MapID m) {
3255+
// Without operands there is no EGPR to encode, so REX2 is never needed.
3256+
emit_rex(size);
3257+
emit_legacy_map_escape(m);
3258+
}
3259+
3260+
template <class P1>
3261+
void emit_rex2_or_rex(P1 p1, int size, Rex2MapID m) {
3262+
#ifdef V8_ENABLE_APX_F
3263+
if (needs_rex2_prefix(p1)) {
3264+
if (size == kInt64Size) {
3265+
emit_rex2_64(p1, m);
3266+
} else {
3267+
DCHECK_EQ(size, kInt32Size);
3268+
emit_rex2_32(p1, m);
3269+
}
3270+
return;
3271+
}
3272+
#endif // V8_ENABLE_APX_F
3273+
emit_rex(p1, size);
3274+
emit_legacy_map_escape(m);
3275+
}
3276+
3277+
template <class P1, class P2>
3278+
void emit_rex2_or_rex(P1 p1, P2 p2, int size, Rex2MapID m) {
3279+
#ifdef V8_ENABLE_APX_F
3280+
if (needs_rex2_prefix(p1, p2)) {
3281+
if (size == kInt64Size) {
3282+
emit_rex2_64(p1, p2, m);
3283+
} else {
3284+
DCHECK_EQ(size, kInt32Size);
3285+
emit_rex2_32(p1, p2, m);
3286+
}
3287+
return;
3288+
}
3289+
#endif // V8_ENABLE_APX_F
3290+
emit_rex(p1, p2, size);
3291+
emit_legacy_map_escape(m);
3292+
}
3293+
3294+
void emit_rex2_or_rex_64(Rex2MapID m) {
3295+
emit_rex_64();
3296+
emit_legacy_map_escape(m);
3297+
}
3298+
3299+
template <class P1>
3300+
void emit_rex2_or_rex_64(P1 p1, Rex2MapID m) {
3301+
#ifdef V8_ENABLE_APX_F
3302+
if (needs_rex2_prefix(p1)) {
3303+
emit_rex2_64(p1, m);
3304+
return;
3305+
}
3306+
#endif // V8_ENABLE_APX_F
3307+
emit_rex_64(p1);
3308+
emit_legacy_map_escape(m);
3309+
}
3310+
3311+
template <class P1, class P2>
3312+
void emit_rex2_or_rex_64(P1 p1, P2 p2, Rex2MapID m) {
3313+
#ifdef V8_ENABLE_APX_F
3314+
if (needs_rex2_prefix(p1, p2)) {
3315+
emit_rex2_64(p1, p2, m);
3316+
return;
3317+
}
3318+
#endif // V8_ENABLE_APX_F
3319+
emit_rex_64(p1, p2);
3320+
emit_legacy_map_escape(m);
3321+
}
3322+
3323+
template <class P1>
3324+
void emit_rex2_or_rex_32(P1 p1, Rex2MapID m) {
3325+
#ifdef V8_ENABLE_APX_F
3326+
if (needs_rex2_prefix(p1)) {
3327+
emit_rex2_32(p1, m);
3328+
return;
3329+
}
3330+
#endif // V8_ENABLE_APX_F
3331+
emit_rex_32(p1);
3332+
emit_legacy_map_escape(m);
3333+
}
3334+
3335+
template <class P1, class P2>
3336+
void emit_rex2_or_rex_32(P1 p1, P2 p2, Rex2MapID m) {
3337+
#ifdef V8_ENABLE_APX_F
3338+
if (needs_rex2_prefix(p1, p2)) {
3339+
emit_rex2_32(p1, p2, m);
3340+
return;
3341+
}
3342+
#endif // V8_ENABLE_APX_F
3343+
emit_rex_32(p1, p2);
3344+
emit_legacy_map_escape(m);
3345+
}
3346+
3347+
// As emit_rex2_or_rex_32, but the REX prefix is omitted entirely when it
3348+
// would be all-zero. Only the REX fallback is optional: a REX2 prefix is
3349+
// always emitted when an operand needs one.
3350+
template <class P1>
3351+
void emit_optional_rex2_or_rex_32(P1 p1, Rex2MapID m) {
3352+
#ifdef V8_ENABLE_APX_F
3353+
if (needs_rex2_prefix(p1)) {
3354+
emit_rex2_32(p1, m);
3355+
return;
3356+
}
3357+
#endif // V8_ENABLE_APX_F
3358+
emit_optional_rex_32(p1);
3359+
emit_legacy_map_escape(m);
3360+
}
3361+
3362+
template <class P1, class P2>
3363+
void emit_optional_rex2_or_rex_32(P1 p1, P2 p2, Rex2MapID m) {
3364+
#ifdef V8_ENABLE_APX_F
3365+
if (needs_rex2_prefix(p1, p2)) {
3366+
emit_rex2_32(p1, p2, m);
3367+
return;
3368+
}
3369+
#endif // V8_ENABLE_APX_F
3370+
emit_optional_rex_32(p1, p2);
3371+
emit_legacy_map_escape(m);
3372+
}
3373+
32163374
// Emit vex prefix
32173375
void emit_vex2_byte0() { emit(0xc5); }
32183376
inline void emit_vex2_byte1(XMMRegister reg, XMMRegister v, VectorLength l,

0 commit comments

Comments
 (0)