[LLVMdev] patch for llc/ARM: added mechanism to move switch tables from .text -> .data; also cleanup and documentation
robert muth
robert at muth.org
Wed Jun 24 14:20:57 PDT 2009
Evan:
Sorry for the late follow up, I was out of town last week.
Enclosed please find the updated patch including all
your suggestions and a dejagnus test.
Robert
On Thu, Jun 11, 2009 at 2:27 PM, Evan Cheng <evan.cheng at apple.com> wrote:
>
> On Jun 8, 2009, at 2:42 PM, robert muth wrote:
>
> > On Sun, Jun 7, 2009 at 11:53 PM, Evan Cheng <evan.cheng at apple.com>
> > wrote:
> >>
> >> On Jun 7, 2009, at 6:59 AM, robert muth wrote:
> >>
> >>> On Sat, Jun 6, 2009 at 4:51 PM, Evan Cheng<evan.cheng at apple.com>
> >>> wrote:
> >>>> +cl::opt<std::string> FlagJumpTableSection("jumptable-section",
> >>>> +
> >>>> cl::init(".data.jtab"));
> >>>> +
> >>>
> >>> I thought it would be nice to group all the jumptables together.
> >>> But as long as it stays configurable, I am fine to change the
> >>> default
> >>> to ".data".
> >>
> >> There is already a TargetAsmInfo::JumpTableDataSection. Why not just
> >> use that?
> >
> > Nice find. I will use that and possible change the current setting,
> > ".const", if it does not work,
> >
> >>>
> >>>> Is this necessary? Why not just put it in the normal data section?
> >>>> Also "outline" jumptable seems like a strange term. Can you think
> >>>> of
> >>>> something else?
> >>>
> >>>
> >>> Yes, that is a tough one. How about "NonInline" instead.
> >>
> >> Or "OutOfLine"?
> >
> > That works for me.
> >
> >> Please add a test case as well. Thanks,
> >
> > I am not sure how to go about testing.
>
> For this please just add a test case to the llvm dejagnu tests.
>
> thanks,
>
> Evan
>
> >
> > I have a script that compiles a bunch of test
> > programs (gnu c torture test, etc.) and then runs the executables
> > on qemu. I run this script with and without my flags and
> > make sure that I do not introduce any new problems
> > -- there are currently plenty of vararg issues.
> >
> > I was thinking of sending this script to the list
> > or maybe checking it into the llvm tree.
> >
> > The key is that whatever tests there are they should just
> > be run with and without the new flag.
> > How do you run backend tests?
> >
> > Robert
> >
> >
> >
> >
> >> Evan
> >>
> >>>
> >>> Robert
> >>>
> >>>> Thanks,
> >>>> Evan
> >>>>
> >>>> Sent from my iPhone
> >>>> On Jun 2, 2009, at 6:26 PM, robert muth <robert at muth.org> wrote:
> >>>>
> >>>> Hi:
> >>>>
> >>>> This is my first patch submission. Hopefully, this is the proper
> >>>> the
> >>>> protocol.
> >>>> Attached is a patch for the llc ARM backend:
> >>>>
> >>>> Added mechanism to generate switch table in a data section
> >>>> rather than having it interleaved with the code.
> >>>> This is controlled by command line flags and off by default.
> >>>> Also, tried to document and improve the code where I modified it.
> >>>>
> >>>> Robert
> >>>>
> >>>> <llc.patch.txt>
> >>>>
> >>>> _______________________________________________
> >>>> LLVM Developers mailing list
> >>>> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> >>>> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >>>>
> >>>> _______________________________________________
> >>>> LLVM Developers mailing list
> >>>> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> >>>> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >>>>
> >>>>
> >>>
> >>> _______________________________________________
> >>> LLVM Developers mailing list
> >>> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> >>> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >>
> >> _______________________________________________
> >> LLVM Developers mailing list
> >> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> >> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >>
> >
> > _______________________________________________
> > LLVM Developers mailing list
> > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20090624/01802d9c/attachment.html>
-------------- next part --------------
Index: test/CodeGen/ARM/switch.ll
===================================================================
--- test/CodeGen/ARM/switch.ll (revision 0)
+++ test/CodeGen/ARM/switch.ll (revision 0)
@@ -0,0 +1,82 @@
+; Note: jump table has 7 entries
+; RUN: llvm-as < %s | llc -march=arm | \
+; RUN: grep .long | count 7
+; Note: no section change
+; RUN: llvm-as < %s | llc -march=arm | \
+; RUN: grep .text | count 1
+; Note: no data section
+; RUN: llvm-as < %s | llc -march=arm | \
+; RUN: not grep .data
+;
+; Note: jump table has 7 entries + 1 entry for the table start
+; RUN: llvm-as < %s | llc -march=arm -outline-jumptables | \
+; RUN: grep .long | count 8
+; Note one section change
+; RUN: llvm-as < %s | llc -march=arm -outline-jumptables | \
+; RUN: grep .text | count 2
+; Note: has data section
+; RUN: llvm-as < %s | llc -march=arm -outline-jumptables | \
+; RUN: grep .data | count 1
+
+
+define i32 @f(i32 %i) nounwind {
+entry:
+ %i_addr = alloca i32 ; <i32*> [#uses=2]
+ %retval = alloca i32 ; <i32*> [#uses=2]
+ %0 = alloca i32 ; <i32*> [#uses=8]
+ %"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
+ store i32 %i, i32* %i_addr
+ %1 = load i32* %i_addr, align 4 ; <i32> [#uses=1]
+ switch i32 %1, label %bb7 [
+ i32 -2, label %bb
+ i32 -1, label %bb1
+ i32 0, label %bb2
+ i32 1, label %bb3
+ i32 2, label %bb4
+ i32 3, label %bb5
+ i32 4, label %bb6
+ ]
+
+bb: ; preds = %entry
+ store i32 33, i32* %0, align 4
+ br label %bb8
+
+bb1: ; preds = %entry
+ store i32 0, i32* %0, align 4
+ br label %bb8
+
+bb2: ; preds = %entry
+ store i32 7, i32* %0, align 4
+ br label %bb8
+
+bb3: ; preds = %entry
+ store i32 4, i32* %0, align 4
+ br label %bb8
+
+bb4: ; preds = %entry
+ store i32 3, i32* %0, align 4
+ br label %bb8
+
+bb5: ; preds = %entry
+ store i32 15, i32* %0, align 4
+ br label %bb8
+
+bb6: ; preds = %entry
+ store i32 9, i32* %0, align 4
+ br label %bb8
+
+bb7: ; preds = %entry
+ store i32 999, i32* %0, align 4
+ br label %bb8
+
+bb8: ; preds = %bb6, %bb5, %bb4, %bb3, %bb2, %bb1, %bb
+ %2 = load i32* %0, align 4 ; <i32> [#uses=1]
+ store i32 %2, i32* %retval, align 4
+ br label %return
+
+return: ; preds = %bb8
+ %retval9 = load i32* %retval ; <i32> [#uses=1]
+ ret i32 %retval9
+}
+
+
Index: lib/Target/ARM/ARMISelLowering.h
===================================================================
--- lib/Target/ARM/ARMISelLowering.h (revision 74072)
+++ lib/Target/ARM/ARMISelLowering.h (working copy)
@@ -202,9 +202,10 @@
/// make the right decision when generating code for different targets.
const ARMSubtarget *Subtarget;
- /// ARMPCLabelIndex - Keep track the number of ARM PC labels created.
- ///
+ /// ARMPCLabelIndex - Keep track of the number of ARM PC labels created.
unsigned ARMPCLabelIndex;
+ /// ARMJumpTableIndex - Keep track of the number ofJump Tables
+ unsigned ARMJumpTableIndex;
void addTypeForNEON(MVT VT, MVT PromotedLdStVT, MVT PromotedBitwiseVT);
void addDRTypeForNEON(MVT VT);
Index: lib/Target/ARM/ARMConstantPoolValue.h
===================================================================
--- lib/Target/ARM/ARMConstantPoolValue.h (revision 74072)
+++ lib/Target/ARM/ARMConstantPoolValue.h (working copy)
@@ -25,6 +25,7 @@
enum ARMCPKind {
CPValue,
CPNonLazyPtr,
+ CPDataSegmentJumpTable,
CPStub
};
}
@@ -54,7 +55,6 @@
ARMConstantPoolValue(GlobalValue *GV, ARMCP::ARMCPKind Kind,
const char *Modifier);
-
GlobalValue *getGV() const { return GV; }
const char *getSymbol() const { return S; }
const char *getModifier() const { return Modifier; }
@@ -63,6 +63,9 @@
unsigned getLabelId() const { return LabelId; }
bool isNonLazyPointer() const { return Kind == ARMCP::CPNonLazyPtr; }
bool isStub() const { return Kind == ARMCP::CPStub; }
+ bool isValue() const { return Kind == ARMCP::CPValue; }
+ bool isDataSegmentJumpTable() const {
+ return Kind == ARMCP::CPDataSegmentJumpTable; }
unsigned char getPCAdjustment() const { return PCAdjust; }
virtual int getExistingMachineCPValue(MachineConstantPool *CP,
Index: lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp
===================================================================
--- lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp (revision 74072)
+++ lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp (working copy)
@@ -33,13 +33,17 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Mangler.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <cctype>
+#include <sstream>
using namespace llvm;
+extern cl::opt<bool> FlagOutlineJumpTables;
+
STATISTIC(EmittedInsts, "Number of machine instrs printed");
namespace {
@@ -141,6 +145,7 @@
/// EmitMachineConstantPoolValue - Print a machine constantpool value to
/// the .s file.
virtual void EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) {
+ // NOTE: A lot of this code is replicated in ARMConstantPoolValue::print
printDataDirective(MCPV->getType());
ARMConstantPoolValue *ACPV = static_cast<ARMConstantPoolValue*>(MCPV);
@@ -157,8 +162,13 @@
} else if (ACPV->isStub()) {
FnStubs.insert(Name);
printSuffixedName(Name, "$stub");
- } else
+ } else if (ACPV->isDataSegmentJumpTable()) {
+ // requires synchronization with code (grep for "$jumptable$")
+ O << Name << "$jumptable$" << ACPV->getLabelId();
+ } else {
+ assert(ACPV->isValue() && "unknown CP kind");
O << Name;
+ }
if (ACPV->hasModifier()) O << "(" << ACPV->getModifier() << ")";
if (ACPV->getPCAdjustment() != 0) {
O << "-(" << TAI->getPrivateGlobalPrefix() << "PC"
@@ -757,12 +767,31 @@
}
}
+
void ARMAsmPrinter::printJTBlockOperand(const MachineInstr *MI, int OpNo) {
- const MachineOperand &MO1 = MI->getOperand(OpNo);
- const MachineOperand &MO2 = MI->getOperand(OpNo+1); // Unique Id
- unsigned JTI = MO1.getIndex();
- O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
- << '_' << JTI << '_' << MO2.getImm() << ":\n";
+ std::stringstream prefix;
+
+ const unsigned JTI = MI->getOperand(OpNo).getIndex();
+ const unsigned uid = MI->getOperand(OpNo+1).getImm();
+
+ if (FlagOutlineJumpTables) {
+ // needs to be synchronized with ARMConstantPoolValue.cpp
+ prefix << ".T$jumptable$" << uid;
+ } else {
+ // needs to be synchronized with other places in this file
+ prefix << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
+ << '_' << JTI << '_' << uid;
+ }
+
+ if (FlagOutlineJumpTables) {
+ // switch out of text section
+ O << TAI->getJumpTableDataSection();
+ O << ".align 4\n";
+ O << "\n\n";
+ }
+
+ // the table label
+ O << prefix.str() << ":\n";
const char *JTEntryDirective = TAI->getJumpTableDirective();
if (!JTEntryDirective)
@@ -777,24 +806,28 @@
for (unsigned i = 0, e = JTBBs.size(); i != e; ++i) {
MachineBasicBlock *MBB = JTBBs[i];
if (UseSet && JTSets.insert(MBB).second)
- printPICJumpTableSetLabel(JTI, MO2.getImm(), MBB);
+ printPICJumpTableSetLabel(JTI, uid, MBB);
O << JTEntryDirective << ' ';
if (UseSet)
- O << TAI->getPrivateGlobalPrefix() << getFunctionNumber()
- << '_' << JTI << '_' << MO2.getImm()
- << "_set_" << MBB->getNumber();
+ O << prefix.str() << "_set_" << MBB->getNumber();
else if (TM.getRelocationModel() == Reloc::PIC_) {
printBasicBlockLabel(MBB, false, false, false);
// If the arch uses custom Jump Table directives, don't calc relative to JT
if (!TAI->getJumpTableDirective())
- O << '-' << TAI->getPrivateGlobalPrefix() << "JTI"
- << getFunctionNumber() << '_' << JTI << '_' << MO2.getImm();
+ O << '-' << prefix.str();
} else
printBasicBlockLabel(MBB, false, false, false);
if (i != e-1)
O << '\n';
}
+
+ if (FlagOutlineJumpTables) {
+ // switch back into the text section
+ O << "\n\n";
+ O << ".text\n";
+ O << "\n\n";
+ }
}
Index: lib/Target/ARM/ARMTargetAsmInfo.cpp
===================================================================
--- lib/Target/ARM/ARMTargetAsmInfo.cpp (revision 74072)
+++ lib/Target/ARM/ARMTargetAsmInfo.cpp (working copy)
@@ -52,6 +52,7 @@
SetDirective = "\t.set\t";
ProtectedDirective = NULL;
HasDotTypeDotSizeDirective = false;
+ JumpTableDataSection = "\t.section .data,\"aMS\",%progbits,1\n";
SupportsDebugInformation = true;
}
@@ -86,6 +87,7 @@
StaticCtorsSection = "\t.section .ctors,\"aw\",%progbits";
StaticDtorsSection = "\t.section .dtors,\"aw\",%progbits";
}
+ JumpTableDataSection = "\t.section .data,\"aMS\",%progbits,1\n";
SupportsDebugInformation = true;
}
Index: lib/Target/ARM/ARMISelLowering.cpp
===================================================================
--- lib/Target/ARM/ARMISelLowering.cpp (revision 74072)
+++ lib/Target/ARM/ARMISelLowering.cpp (working copy)
@@ -36,9 +36,16 @@
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/ADT/VectorExtras.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MathExtras.h"
+
using namespace llvm;
+
+
+cl::opt<bool> FlagOutlineJumpTables("outline-jumptables",
+ cl::desc("move jumptables from text to data"));
+
static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags,
@@ -1686,27 +1693,119 @@
return Res;
}
-SDValue ARMTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) {
+
+
+// Similar to LowerBR_JT_Inline except that the jumptable
+// is moved to the data segment.
+// This causes a extra load to access the table but keeps the
+// text segment small and avoids some issues with generating PIC
+// Needs to be activated with a commandline flag.
+static SDValue LowerBR_JT_OutOfLine(SDValue Op,
+ SelectionDAG &DAG,
+ unsigned Num,
+ MVT PTy) {
+
SDValue Chain = Op.getOperand(0);
- SDValue Table = Op.getOperand(1);
- SDValue Index = Op.getOperand(2);
- DebugLoc dl = Op.getDebugLoc();
- MVT PTy = getPointerTy();
- JumpTableSDNode *JT = cast<JumpTableSDNode>(Table);
+ const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op.getOperand(1));
+ const SDValue Index = Op.getOperand(2);
+
+
+ const DebugLoc dl = Op.getDebugLoc();
+
+ const SDValue UId = DAG.getConstant(Num, PTy);
+ const SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy);
+
+ // create a new global symbol for the jumptable
+ ARMConstantPoolValue *CPV = new ARMConstantPoolValue(".T", Num,
+ ARMCP::CPDataSegmentJumpTable);
+ const SDValue CPAddr = DAG.getTargetConstantPool(CPV, PTy, 4);
+
+ // An ARM idiosyncrasy: wrap each constant pool entry before accessing it
+ const SDValue Wrapper = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
+
+ // Load Table start from constan pool
+ const SDValue Table = DAG.getLoad(PTy, dl, DAG.getEntryNode(), Wrapper, NULL, 0);
+
+ // table entries are 4 bytes, so multiple index by 4
+ const SDValue ScaledIndex = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, PTy));
+
+ // add scaled index to table beginning
+ const SDValue TabEntryAddr = DAG.getNode(ISD::ADD, dl, PTy, ScaledIndex, Table);
+ const SDValue JumpTarget = DAG.getLoad(PTy, dl, Chain, TabEntryAddr, NULL, 0);
+
+ return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, JumpTarget, JTI, UId);
+}
+
+
+static SDValue LowerBR_JT_Inline(SDValue Op,
+ SelectionDAG &DAG,
+ bool isPIC,
+ MVT PTy) {
+ // The Jumptable idiom we are aiming for looks somthing like this:
+ //
+ // .set PCRELV0, (.LJTI9_0_0-(.LPCRELL0+8))
+ // .LPCRELL0:
+ // add r3, pc, #PCRELV0
+ // ldr pc, [r3, +r0, lsl #2]
+ // .LJTI9_0_0:
+ // .long .LBB9_2
+ // .long .LBB9_5
+ // .long .LBB9_7
+ // .long .LBB9_4
+ // .long .LBB9_8
+ //
+ // In pic mode the table entries are relative to table beginning
+ // requiring and extra addition
+ //
+ // The code generation logic for ARMISD::BR_JT will also
+ // emit the table (c.f. ARMAsmPrinter::printJTBlockOperand())
+ // Also check "def BR_JTm" in ARMInstrInfo.td
+
+ // allocate constant pool entry
ARMFunctionInfo *AFI = DAG.getMachineFunction().getInfo<ARMFunctionInfo>();
- SDValue UId = DAG.getConstant(AFI->createJumpTableUId(), PTy);
- SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy);
- Table = DAG.getNode(ARMISD::WrapperJT, dl, MVT::i32, JTI, UId);
- Index = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, PTy));
- SDValue Addr = DAG.getNode(ISD::ADD, dl, PTy, Index, Table);
- bool isPIC = getTargetMachine().getRelocationModel() == Reloc::PIC_;
- Addr = DAG.getLoad(isPIC ? (MVT)MVT::i32 : PTy, dl,
- Chain, Addr, NULL, 0);
- Chain = Addr.getValue(1);
- if (isPIC)
- Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr, Table);
- return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, Addr, JTI, UId);
+
+ SDValue Chain = Op.getOperand(0);
+
+ const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op.getOperand(1));
+ const SDValue Index = Op.getOperand(2);
+ const DebugLoc dl = Op.getDebugLoc();
+
+
+ const SDValue UId = DAG.getConstant(AFI->createJumpTableUId(), PTy);
+
+ const SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy);
+
+ // this uses pcrel magic to materialize the table start address
+ const SDValue Table = DAG.getNode(ARMISD::WrapperJT, dl, MVT::i32, JTI, UId);
+
+ // table entries are 4 bytes, so multiple index by 4
+ const SDValue ScaledIndex = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, PTy));
+
+ // add scaled index to table beginning
+ const SDValue TabEntryAddr = DAG.getNode(ISD::ADD, dl, PTy, ScaledIndex, Table);
+ SDValue JumpTarget;
+ if (isPIC) {
+ JumpTarget = DAG.getLoad((MVT)MVT::i32, dl, Chain, TabEntryAddr, NULL, 0);
+ Chain = JumpTarget.getValue(1);
+ // the table entry is not the actual target but relative to other stuff
+ // TODO: explain the exact magic here
+ JumpTarget = DAG.getNode(ISD::ADD, dl, PTy, JumpTarget, Table);
+ } else {
+ JumpTarget = DAG.getLoad(PTy, dl, Chain, TabEntryAddr, NULL, 0);
+ Chain = JumpTarget.getValue(1);
+ }
+
+ return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, JumpTarget, JTI, UId);
+}
+
+SDValue ARMTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) {
+ if (FlagOutlineJumpTables) {
+ return LowerBR_JT_OutOfLine(Op, DAG, ++ARMJumpTableIndex, getPointerTy());
+ } else {
+ const bool isPIC = getTargetMachine().getRelocationModel() == Reloc::PIC_;
+ return LowerBR_JT_Inline(Op, DAG, isPIC, getPointerTy());
+ }
}
static SDValue LowerFP_TO_INT(SDValue Op, SelectionDAG &DAG) {
Index: lib/Target/ARM/ARMConstantPoolValue.cpp
===================================================================
--- lib/Target/ARM/ARMConstantPoolValue.cpp (revision 74072)
+++ lib/Target/ARM/ARMConstantPoolValue.cpp (working copy)
@@ -84,12 +84,26 @@
}
void ARMConstantPoolValue::print(raw_ostream &O) const {
+ // NOTE: this is not used for codegeneration, moreover
+ // some of the logic is replicated in
+ // EmitMachineConstantPoolValue()
+
if (GV)
O << GV->getName();
else
O << S;
- if (isNonLazyPointer()) O << "$non_lazy_ptr";
- else if (isStub()) O << "$stub";
+
+ if (isNonLazyPointer()) {
+ O << "$non_lazy_ptr";
+ } else if (isStub()) {
+ O << "$stub";
+ } else if (isDataSegmentJumpTable()) {
+ // requires synchronization with ARMAsmPrinter.cpp
+ O << "$jumptable$" << LabelId;
+ } else {
+ assert(isValue() && "unknown CP kind");
+ }
+
if (Modifier) O << "(" << Modifier << ")";
if (PCAdjust != 0) {
O << "-(LPC" << LabelId << "+" << (unsigned)PCAdjust;
More information about the llvm-dev
mailing list