- Make code clearer by separating the logic of setting bits from the logic of how a prefix is encoded
- Extract common code into functions to avoid code duplication
- Return a enum rather a boolean to ehance scalability and uniform the behavior of functions
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | I don't know that it makes sense to make returning the bool for HasREX worse by spreading it to more functions. None of the other prefixes are needed and I'm not sure there's sufficient evidence they ever will be. And if they were needed would what type of prefix be enough information? A bool &HasREX passed to emitOpcodePrefix and emitREXPrefix feels cleaner to me. I never liked the returned bool for this. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | I know a little about it. When X86 introduces a bunch of new instructions, it usually extends the prefix. For example, And when we introduce new instructions, a new relocation may be needed. Here is the example for REX Linker can do relocation optimization based on the relocation. From my understanding, if no new relocation were not added for the new instructions, the optimization could be done in an incorrect way silently. Only a bool HasREX was defined here b/c the current interested instructions are only REX-encoded. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | Got your idea. Let me to pass the enum by reference rather than a return value. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | REX is older than the VEX, XOP, EVEX prefixes so it doesn’t seem common to need the new relocation. It was cheaper to compute I’d say we could determine if it has REX in the fixup code but we have to inspect multiple operands. If we need to know about a prefix for a future location that one might be cheaper to compute. This feels like we’re trying to solve a future problem that might never exist. Is it really worth it? | |
1278 | *relocation |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | It’s also hard to add new relocations anyway since you would need a new linker that understands it. Or a command line to opt into it. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | I just gave a try. But passing the enum as argument increases the length of arglists for four functions: emitREXPrefix, emitVEXOpcodePrefix, emitOpcodePrefix and emitPrefixImpl longer, which made me nervous. I don't even know which order I should use for the parameters for the functions. I remembered that we once refined the code in this file to reduce the parameters, so I prefer the "return value" version. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | Yes, I was talking about that if we know more info about prefix, we can easily extend the relocation type in X86MCCodeEmitter::emitMemModRMByte. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
782–783 | Need rewrite the comment |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1278 | I think it's worthy b/c we uniform the behavior of functions emit*Prefix*, and at the same time, there is almost no extra cost. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1148 | The names of these functions is confusing. getKind followed by setKind looks odd. I would naively expect setKind to set the thing that getKind returns. So getKind followed by setKind looks redundant. |
Address review comments
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
1148 | You're right. I re-designed the interface to setLowerBound, determineOptimalKind. |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
784–786 | Format. | |
866–883 | Should be better to move below logic into X86OpcodePrefix ? Then we can simplify the function to: X86OpcodePrefix Prefix(*Ctx.getRegisterInfo(), TSFlags); Prefix.emit(OS); return Prefix.getKind(); | |
871 | Should be better to use setEncoding? |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
784–786 | Will do | |
866–883 | We can not b/c we still need to read the operands the instruction before the final emit. "Make code clearer by separating the logic of setting bits from the logic of how a prefix is encoded" | |
871 | I think setLowerBound is more clear than setEncoding here b/c VEX and REX are determined at the last stage. e.g |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
874 | explicit* |
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
140 | I'm not sure if it makes sense to have this class understand MRI and MI. I kind of think it should only receive the register encoding. Especially when you look at funcions like setRR2 that now end up looking up the encoding twice because it nests calls to setR and setR2. |
Thinking.... What if we took this further and had an "encodeable instruction" object that contains the prefix fields and the modrm fields that we build by walking the operands and format once. Then we would only have 1 switch on format instead of the 3 we have now. Then we use that to emit the prefix, the opcode, and the modrm byte.
This sounds good! Let me give a try.
llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp | ||
---|---|---|
140 | I agree the name X86OpcodePrefix doesn't sound like much to understand MI, we should have a "Helper" suffix. Encoding twice is not a big issue. We can easily extract a function "getX86RegEncoding" to avoid this. I am thinking your new proposal "encodeable instruction" |
@craig.topper I think it's doable with some space cost. e.g, we need to reserve some bits in "encodable instruction" to represent the "REG", "MOD", "RM" and "SIB" and It's even true when we only would like to emitPrefix.
The code change for encodable instruction is quite a big and can share some common code of this patch. I suggest we land this first and possibly implement "encodable instruction" based on it.
Hi,
I noticed that if I run lit tests with ubsan built binaries with this patch there are many many tests failing like
../lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp:222:19: runtime error: left shift of negative value -1
I think it's false positive?
~R << 7
R is an unsigned, so ~R is unsigned too. Although the value of ~R may be 0xffffffff. But I remember the left shift operation on unsigned is always well defined as long as the shift not exceed the size of unsigned.
I remember the << operator is defined to shift signed int in C language. There's no relationship with the type of shifted value.
I am not an expert in latest C standard. But we are in C++
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf
8.5.7 Shift operators [expr.shift]
1 The shift operators << and >> group left-to-right.
shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The
type of the result is that of the promoted left operand. The behavior is undefined if the right operand is
negative, or greater than or equal to the length in bits of the promoted left operand.
2 The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned
type, the value of the result is E1 × 2
E2, reduced modulo one more than the maximum value representable in
the result type. Otherwise, if E1 has a signed type and non-negative value, and E1 × 2
E2 is representable
in the corresponding unsigned type of the result type, then that value, converted to the result type, is the
resulting value; otherwise, the behavior is undefined.
According to the rule, unsigned does not need to be promoted and the shift operation is still on a unsigned.
I think there is promotion to int due to the ~
https://stackoverflow.com/questions/32529080/should-bit-fields-less-than-int-in-size-be-the-subject-of-integral-promotion
Hi, the LLDB sanitized bots are also failing with this error https://green.lab.llvm.org/green/view/LLDB/job/lldb-cmake-sanitized/3335/console
I think uabelho is right about the promotion. According to the standard: "The operand of ~ shall have integral or unscoped enumeration type; the result is the ones’ complement of its operand. Integral promotions are performed."
I'm not sure if it makes sense to have this class understand MRI and MI. I kind of think it should only receive the register encoding.
Especially when you look at funcions like setRR2 that now end up looking up the encoding twice because it nests calls to setR and setR2.