With the ROPI and RWPI relocation models we can't always have pointers
to global data or functions in constant data, so don't try to convert
switches into lookup tables if the type of the PHI is a pointer type. We
can still safely emit lookup tables of any non-pointer type.
Details
Diff Detail
Event Timeline
Checking the type doesn't make sense; consider what happens if you have a PHI node with an operand i32 ptrtoint (i32* @g).
lib/Transforms/Utils/SimplifyCFG.cpp | ||
---|---|---|
5155 | Should we be calling ValidLookupTableConstant here instead of calling shouldBuildLookupTablesForConstant directly? |
- Remove old check of shouldBuildLookupTablesForConstant
- Re-order ValidLookupTableConstant to do target-dependent checks last
This change doesn't make sense to me. At most, ROPI/RWPI should enforce creation of PIC jump tables. There are some issues with the way we currently generate those, which is biting us on PPC64, but it is still the correct way forward.
The problem here is that this pass can create new global variables, which the backend can't recognise as jump tables (they may not actually be jump tables, as they could be tables of pointers to data).
There are a few different ways that ROPI/RWPI can be made to support pointers to globals in source code, with varying requirements on the rest of the compiler:
- Don't do anything special, the user will have to modify their code to avoid global variables initialised to the address of a global or function. This is currently done by clang, and requires that global pointers are not introduced by LLVM.
- Add dynamic initialisers in the frontend, called from the .init_array section. This is currently done by armclang (our clang-based bare-metal toolchain). We chose this option because it allows moving constants to a read-write section if they need dynamic initialisation, and emitting diagnostics for some C constructs that may not work properly with ROPI/RWPI. This also requires that global pointers are not introduced by LLVM.
- Use a small dynamic loader to fix-up any global pointers. This requires linker and runtime library support, I'm not aware of any implementations of this. This wouldn't be able to fix up constants when they are loaded into ROM. This would allow the compiler to introduce global pointers, but would require them to be in a read-write section, so that their value can be adjusted by the loader.
This change supports the first two, the other option would be to modify this pass to emit all lookup tables containing pointers as variables rather than constants (and possibly modify globalopt to avoid undoing that transformation). I prefer turning off this optimisation for pointers in ROPI/RWPI modes, as it avoids embedding too much knowledge about a rather unusual ARM-specific ABI in the midend.
lib/Transforms/Utils/SimplifyCFG.cpp | ||
---|---|---|
4441 | I think this needs to be something more like: if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { if (!CE->isGEPWithNoNotionalOverIndexing()) return false; if (!ValidLookupTableConstant(CE->getOperand(0))) return false; } Specifically, you need to recurse over GEPs to make sure they aren't doing anything that can't be represented in a global variable. (I guess it's an existing issue, but worth solving while you're here.) |
lib/Transforms/Utils/SimplifyCFG.cpp | ||
---|---|---|
4441 | Good catch, I'll put up a separate patch for that once I can find a good test for it. |
It would seem more natural to do all the target-independent checks first, and then defer to TTI for the rest.