This is an archive of the discontinued LLVM Phabricator instance.

[ELF][MIPS] Use MIPS R6 `sigrie` as a trap instruction
ClosedPublic

Authored by atanasyan on Nov 6 2018, 7:04 AM.

Details

Summary

Current value using as a trap instruction (0xefefefef) is not a good choice for MIPS because it's a valid MIPS instruction swc3 $15,-4113(ra). This patch replaces 0xefefefef by 0x04170001. For all MIPS ISA revisions before R6, this value is just invalid instruction. Starting from MIPS R6 it's a valid instruction sigrie 1 which signals a Reserved Instruction exception.

mips-traps.s test case is added to test trap encoding. Other test cases are modified to remove redundant checking.

Diff Detail

Repository
rLLD LLVM Linker

Event Timeline

atanasyan created this revision.Nov 6 2018, 7:04 AM
ruiu accepted this revision.Nov 6 2018, 2:19 PM

LGTM

This revision is now accepted and ready to land.Nov 6 2018, 2:19 PM
jrtc27 requested changes to this revision.Nov 6 2018, 2:29 PM
jrtc27 added inline comments.
ELF/Arch/Mips.cpp
61

Maybe I'm being stupid, but won't this do the wrong thing on a big-endian host? If, say, we're building for mips64 on mips64, Config->IsLE and sys::IsLittleEndianHost will both be false, so we'll take the write32le path, which will write 0x04170001 (represented in-memory natively as big-endian) as a little-endian value, i.e. incorrectly byte-swapping? It seems to me like this should be just a check for if (Config->IsLE)?

This revision now requires changes to proceed.Nov 6 2018, 2:29 PM
atanasyan added inline comments.Nov 6 2018, 2:46 PM
ELF/Arch/Mips.cpp
61

Thanks a lot. The only remaining question - how could I write this nice piece of code? :)

ruiu added a comment.Nov 6 2018, 3:06 PM

Maybe we probably should change the type of TrapInstr from uint32_t to uint8_t[4]?

jrtc27 added inline comments.Nov 6 2018, 3:07 PM
ELF/Arch/Mips.cpp
61

I went and verified this because, despite having many dealings with it, it's too easy to get confused about endianness-related code. So, yes, my belief was correct, and I didn't inadvertently mislead you :) Running on powerpc64-linux-gnu:

-- Testing: 1 tests, 1 threads --
FAIL: lld :: ELF/mips-got16-relocatable.s (1 of 1)
******************** TEST 'lld :: ELF/mips-got16-relocatable.s' FAILED ********************
Script:
--
: 'RUN: at line 5';   /home/jrtc27/src/llvm/Build/bin/llvm-mc -filetype=obj -triple=mips-unknown-linux -mcpu=mips32r6 -o /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp.o /home/jrtc27/src/llvm/llvm/tools/lld/test/ELF/mips-got16-relocatable.s
: 'RUN: at line 6';   /home/jrtc27/src/llvm/Build/bin/ld.lld -r -o /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp.o /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp.o
: 'RUN: at line 7';   /home/jrtc27/src/llvm/Build/bin/llvm-objdump -d -r /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp | /home/jrtc27/src/llvm/Build/bin/FileCheck -check-prefix=OBJ /home/jrtc27/src/llvm/llvm/tools/lld/test/ELF/mips-got16-relocatable.s
: 'RUN: at line 8';   /home/jrtc27/src/llvm/Build/bin/ld.lld -shared -o /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp.so /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp
: 'RUN: at line 9';   /home/jrtc27/src/llvm/Build/bin/llvm-objdump -d /home/jrtc27/src/llvm/Build/tools/lld/test/ELF/Output/mips-got16-relocatable.s.tmp.so | /home/jrtc27/src/llvm/Build/bin/FileCheck -check-prefix=SO /home/jrtc27/src/llvm/llvm/tools/lld/test/ELF/mips-got16-relocatable.s
--
Exit Code: 1

Command Output (stderr):
--
/home/jrtc27/src/llvm/llvm/tools/lld/test/ELF/mips-got16-relocatable.s:17:13: error: OBJ-NEXT: expected string not found in input
# OBJ-NEXT: 8: 04 17 00 01 sigrie 1
            ^
<stdin>:10:2: note: scanning from here
 8: 01 00 17 04 <unknown>
 ^

--

********************
Testing Time: 0.22s
********************
Failing Tests (1):
    lld :: ELF/mips-got16-relocatable.s

  Unexpected Failures: 1
jrtc27 added a comment.Nov 6 2018, 3:12 PM

Maybe we probably should change the type of TrapInstr from uint32_t to uint8_t[4]?

That would make sense to clearly convey it's in the target endianness rather than the host; it's certainly a little surprising to have a uint32_t in the "wrong" endianness floating around. My only concern with making it an array is whether it's clear that you need to *repeat* the trap instruction if it's 1 or 2 bytes, but that same argument could be made for the current uint32_t, so probably irrelevant.

ruiu added a comment.Nov 6 2018, 3:15 PM

Yeah, we could alternatively represent a trap instruction as an ArrayRef<uint8_t> which can vary in size, but that's somewhat minor point, and I believe always writing 4 bytes using memcpy is much faster than copying ArrayRef contents because the former can be highly optimized by a compiler that can inline memcpy function.

jrtc27 added a comment.Nov 6 2018, 3:38 PM

Yeah, we could alternatively represent a trap instruction as an ArrayRef<uint8_t> which can vary in size, but that's somewhat minor point, and I believe always writing 4 bytes using memcpy is much faster than copying ArrayRef contents because the former can be highly optimized by a compiler that can inline memcpy function.

Yeah, I considered that as a possibility, but it doesn't seem worth it. Your uint8_t[4] proposal seems like the best option overall to me, and can always be revisited if it turns out to be a problem (I don't suppose we'll be seeing an IA-64 backend needing 16 bytes for its instruction!).

ELF/Arch/Mips.cpp
61

... and finally if I change this line to just if (Config->IsLE) it does indeed pass as expected on powerpc64-linux-gnu, so LGTM from my (unimportant) point of view once that's fixed. Alternatively you could use your Config->IsLE == sys::IsLittleEndianHost check, but explicitly do the byte swapping when they don't match (i.e. your write32le becomes the identity, and write32be becomes a byte swap, but both inlined and constant propagated), like the PPC64 target.

ruiu added a comment.Nov 6 2018, 3:41 PM

Or maybe std::array<uint8_t, 4> as it allows you to initialize it by writing TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc} (which you cannot do if TrapInstr is a uint8_t[4]).

jrtc27 added a comment.Nov 6 2018, 4:01 PM

Or maybe std::array<uint8_t, 4> as it allows you to initialize it by writing TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc} (which you cannot do if TrapInstr is a uint8_t[4]).

Ah, right, of course, I regard that as the same proposal just with nicer syntax! Happy to do the tiny amount of grunt work for that tomorrow if you both have better things to do with your time.

atanasyan updated this revision to Diff 174105.Nov 14 2018, 2:57 PM
atanasyan edited the summary of this revision. (Show Details)
  • Add a separate test case to check trap instruction encoding in case of bi and little endian.
  • Use TrapInstr array.
ruiu added inline comments.Nov 14 2018, 3:45 PM
ELF/Arch/Mips.cpp
61

Let's not handle four byte sequence as one integer because it is very confusing. I'd write

if (Config->IsLE)
  TrapInstr = {....};
else
  TrapInstr = {....};
jrtc27 added inline comments.Nov 14 2018, 3:54 PM
ELF/Arch/Mips.cpp
61

To me this actually seems the most sensible way of writing it; you don't have to care about endianness, and just write the instruction as a literal integer. This is exactly like how you write the PLT entry on various architectures, and both are writing instructions to byte arrays.

ruiu accepted this revision.Nov 14 2018, 8:19 PM
ruiu added inline comments.
ELF/Arch/Mips.cpp
61

OK, then LGTM.

This revision was not accepted when it landed; it landed in state Needs Review.Nov 15 2018, 9:33 PM
This revision was automatically updated to reflect the committed changes.

Thanks for review.