This is an archive of the discontinued LLVM Phabricator instance.

[mips] Compute MaxCallFrame size early on
Needs RevisionPublic

Authored by smaksimovic on Jan 25 2018, 6:35 AM.

Details

Summary

Use computeMaxCallFrameSize() introduced in D32622
to calculate MaxCallFrameSize after instruction selection.

Test that the value is computed before the Prologue/Epilogue pass
after which MaxCallFrameSize will have been computed anyway.

Diff Detail

Event Timeline

smaksimovic created this revision.Jan 25 2018, 6:35 AM

Correct wrong character starting a comment in test file

sdardis accepted this revision.Jan 26 2018, 5:31 AM

LGTM.

This revision is now accepted and ready to land.Jan 26 2018, 5:31 AM

Looking again at the tests reported in the upstream issue I found that with these changes DebugInfo/Mips/dsr-fixed-objects.ll fails still.
The reason being is that the frame register is queried with getFrameRegister() ( which ultimately calls getMaxCallFrameSize() ) inside SelectionDAGISel::runOnMachineFunction() prior to calling finalizeLowering() which calculates maxCallFrameSize.

We could call the function which computes the maxCallFrameSize in MipsDAGToDAGISel::runOnMachineFunction() prior to running selectionDAG which would compute maxCallFrameSize, albeit erroneously.
Computing maxCallFrameSize afterwards finalizeLowering() does get us the correct value, but seems like a hacky way to get around the problem.

Ok, hold off on committing this for now, I want to check this, as I'd missed the debuginfo tests when reviewing this. Good catch though.

lib/Target/Mips/MipsISelLowering.h
373

Nit: whitespace here.

sdardis added a subscriber: MatzeB.Jan 29 2018, 3:15 AM

+CC'ing @MatzeB for additional insight on what needs to be changed.

Found the bug. What's occurring with the debug information test failures with this patch, is that during the insertion of debug instructions for function arguments, it has to determine which register to assign to the debug instruction, see SelectionDAGISel.cpp:505. Here it asks for the frame pointer register which for some RISC like targets by the size of the frame for the function.

It seems to me that we need to compute the max call frame size before that point (SelectionDAGISel.cpp:505) for targets which use call frame setup and destroy pseudo instructions, this however requires changes for every target which implements finalizeLowering() to compute the maxCallFrameSize.

+CC'ing @MatzeB for additional insight on what needs to be changed.

Found the bug. What's occurring with the debug information test failures with this patch, is that during the insertion of debug instructions for function arguments, it has to determine which register to assign to the debug instruction, see SelectionDAGISel.cpp:505. Here it asks for the frame pointer register which for some RISC like targets by the size of the frame for the function.

It seems to me that we need to compute the max call frame size before that point (SelectionDAGISel.cpp:505) for targets which use call frame setup and destroy pseudo instructions, this however requires changes for every target which implements finalizeLowering() to compute the maxCallFrameSize.

Yeah code calling hasFP() early in the process is a real annoyance. You can find me wiggling in the review of https://reviews.llvm.org/D32622 too. At the time it seemed to me like it only influences scheduling heuristic so I went with this hack in the ARM target:

// hasFP ends up calling getMaxCallFrameComputed() which may not be
// available when getPressureLimit() is called as part of
// ScheduleDAGRRList.
bool HasFP = MF.getFrameInfo().isMaxCallFrameSizeComputed()
             ? TFI->hasFP(MF) : true;

instead.

But it looks like you found a bigger problem than the heuristic. If you can find ways for ISel to not do this anymore I would certainly apreciate it!

+CC'ing @MatzeB for additional insight on what needs to be changed.

Found the bug. What's occurring with the debug information test failures with this patch, is that during the insertion of debug instructions for function arguments, it has to determine which register to assign to the debug instruction, see SelectionDAGISel.cpp:505. Here it asks for the frame pointer register which for some RISC like targets by the size of the frame for the function.

It seems to me that we need to compute the max call frame size before that point (SelectionDAGISel.cpp:505) for targets which use call frame setup and destroy pseudo instructions, this however requires changes for every target which implements finalizeLowering() to compute the maxCallFrameSize.

Yeah code calling hasFP() early in the process is a real annoyance. You can find me wiggling in the review of https://reviews.llvm.org/D32622 too. At the time it seemed to me like it only influences scheduling heuristic so I went with this hack in the ARM target:

// hasFP ends up calling getMaxCallFrameComputed() which may not be
// available when getPressureLimit() is called as part of
// ScheduleDAGRRList.
bool HasFP = MF.getFrameInfo().isMaxCallFrameSizeComputed()
             ? TFI->hasFP(MF) : true;

instead.

But it looks like you found a bigger problem than the heuristic. If you can find ways for ISel to not do this anymore I would certainly apreciate it!

Sketch solution: Extend the ISD Opcodes to include READ_FP, as SelectionDAG cannot determine ahead of time if function uses a frame pointer (in the general case). The node takes a single argument, the input chain and returns a pair of results, the current value of the frame pointer and the output chain. The semantics of the node are that it returns the value of the frame pointer at that point if the function is using a frame pointer or the current value of stack pointer if the function does not use a frame pointer. The pseudo instruction is then eliminated early during the finalization of instruction selection by replacing it either with a ISD::COPY or by replacing the users of the result ISD::READ_FP with the physreg directly which now can be computed correctly.

ISel can then use the result of this node without needing the maximum frame size during the selection of any block but then ScheduleDAGRRList would still get the wrong result as it only sees one block at a time. We could track if we have seen basic blocks whose instructions contain call setup/destroy instructions which trigger the creation of reserved call frame or some other target specific requirement for a frame pointer. This however would mean that getPressureLimit would see an inconsistent result for a function.

sdardis requested changes to this revision.Jan 30 2018, 2:05 AM

Marking this as changes required to clear up any confusion.

This revision now requires changes to proceed.Jan 30 2018, 2:05 AM