aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-06-27 18:34:17 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-06-27 18:34:17 +0000
commitdbb6490749e2bb2a514a8a305b8fadc3495183e6 (patch)
treedd42cfd6d9813d5640a01083bdc0eedcb3f862c2
parent483b61a50e7423b063fc26985325f594560b3f7e (diff)
downloadsrc-vendor/llvm-project/llvmorg-10.0.1-rc2-0-g77d76b71d7d.tar.gz
src-vendor/llvm-project/llvmorg-10.0.1-rc2-0-g77d76b71d7d.zip
llvmorg-10.0.1-rc2-0-g77d76b71d7d.
-rw-r--r--clang/lib/CodeGen/TargetInfo.cpp3
-rw-r--r--compiler-rt/lib/builtins/riscv/int_mul_impl.inc31
-rw-r--r--compiler-rt/lib/builtins/riscv/muldi3.S11
-rw-r--r--compiler-rt/lib/builtins/riscv/mulsi3.S23
-rw-r--r--libcxx/include/array2
-rw-r--r--llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp14
-rw-r--r--llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp26
-rw-r--r--llvm/lib/Target/AArch64/AArch64FrameLowering.cpp101
-rw-r--r--llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp7
-rw-r--r--llvm/lib/Target/RISCV/RISCVFrameLowering.cpp4
-rw-r--r--llvm/lib/Target/RISCV/RISCVISelLowering.cpp34
-rw-r--r--llvm/tools/llvm-dwp/DWPError.cpp3
-rw-r--r--llvm/tools/llvm-dwp/DWPError.h23
-rw-r--r--llvm/tools/llvm-dwp/DWPStringPool.h56
-rw-r--r--llvm/tools/llvm-dwp/llvm-dwp.cpp749
-rw-r--r--llvm/tools/llvm-size/llvm-size.cpp894
-rw-r--r--llvm/tools/llvm-strings/llvm-strings.cpp120
17 files changed, 2021 insertions, 80 deletions
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 682ef18da73b..e2380f5581ca 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -9613,7 +9613,8 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
uint64_t Size = getContext().getTypeSize(Ty);
// Pass floating point values via FPRs if possible.
- if (IsFixed && Ty->isFloatingType() && FLen >= Size && ArgFPRsLeft) {
+ if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() &&
+ FLen >= Size && ArgFPRsLeft) {
ArgFPRsLeft--;
return ABIArgInfo::getDirect();
}
diff --git a/compiler-rt/lib/builtins/riscv/int_mul_impl.inc b/compiler-rt/lib/builtins/riscv/int_mul_impl.inc
new file mode 100644
index 000000000000..50951d5f4195
--- /dev/null
+++ b/compiler-rt/lib/builtins/riscv/int_mul_impl.inc
@@ -0,0 +1,31 @@
+//===-- int_mul_impl.inc - Integer multiplication -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers used by __mulsi3, __muldi3.
+//
+//===----------------------------------------------------------------------===//
+
+#if !defined(__riscv_mul)
+ .text
+ .align 2
+
+ .globl __mulxi3
+ .type __mulxi3, @function
+__mulxi3:
+ mv a2, a0
+ mv a0, zero
+.L1:
+ andi a3, a1, 1
+ beqz a3, .L2
+ add a0, a0, a2
+.L2:
+ srli a1, a1, 1
+ slli a2, a2, 1
+ bnez a1, .L1
+ ret
+#endif
diff --git a/compiler-rt/lib/builtins/riscv/muldi3.S b/compiler-rt/lib/builtins/riscv/muldi3.S
new file mode 100644
index 000000000000..9e292e8dd8b9
--- /dev/null
+++ b/compiler-rt/lib/builtins/riscv/muldi3.S
@@ -0,0 +1,11 @@
+//===--- muldi3.S - Integer multiplication routines -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#if __riscv_xlen == 64
+#define __mulxi3 __muldi3
+#include "int_mul_impl.inc"
+#endif
diff --git a/compiler-rt/lib/builtins/riscv/mulsi3.S b/compiler-rt/lib/builtins/riscv/mulsi3.S
index 5464919b26b9..cfafb7a0d7b3 100644
--- a/compiler-rt/lib/builtins/riscv/mulsi3.S
+++ b/compiler-rt/lib/builtins/riscv/mulsi3.S
@@ -1,4 +1,4 @@
-//===--- mulsi3.S - Integer multiplication routines routines ---===//
+//===--- mulsi3.S - Integer multiplication routines -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,22 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#if !defined(__riscv_mul) && __riscv_xlen == 32
- .text
- .align 2
-
- .globl __mulsi3
- .type __mulsi3, @function
-__mulsi3:
- mv a2, a0
- mv a0, zero
-.L1:
- andi a3, a1, 1
- beqz a3, .L2
- add a0, a0, a2
-.L2:
- srli a1, a1, 1
- slli a2, a2, 1
- bnez a1, .L1
- ret
+#if __riscv_xlen == 32
+#define __mulxi3 __mulsi3
+#include "int_mul_impl.inc"
#endif
diff --git a/libcxx/include/array b/libcxx/include/array
index 88e9d57ff783..ddebf9159600 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -359,7 +359,7 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
#ifndef _LIBCPP_HAS_NO_DEDUCTION_GUIDES
template<class _Tp, class... _Args,
- class = typename enable_if<(is_same_v<_Tp, _Args> && ...), void>::type
+ class = _EnableIf<__all<_IsSame<_Tp, _Args>::value...>::value>
>
array(_Tp, _Args...)
-> array<_Tp, 1 + sizeof...(_Args)>;
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index 6aed5796acc6..015b3d99fb0f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -271,8 +271,20 @@ SDValue DAGTypeLegalizer::PromoteIntRes_AtomicCmpSwap(AtomicSDNode *N,
return Res.getValue(1);
}
- SDValue Op2 = GetPromotedInteger(N->getOperand(2));
+ // Op2 is used for the comparison and thus must be extended according to the
+ // target's atomic operations. Op3 is merely stored and so can be left alone.
+ SDValue Op2 = N->getOperand(2);
SDValue Op3 = GetPromotedInteger(N->getOperand(3));
+ if (TLI.getTargetMachine().getTargetTriple().isRISCV()) {
+ // The comparison argument must be sign-extended for RISC-V. This is
+ // abstracted using a new TargetLowering hook in the main LLVM development
+ // branch, but handled here directly in order to fix the codegen bug for
+ // 10.x without breaking the libLLVM.so ABI.
+ Op2 = SExtPromotedInteger(Op2);
+ } else {
+ Op2 = GetPromotedInteger(Op2);
+ }
+
SDVTList VTs =
DAG.getVTList(Op2.getValueType(), N->getValueType(1), MVT::Other);
SDValue Res = DAG.getAtomicCmpSwap(
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 24ab65171a17..368e2100031f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -5490,9 +5490,20 @@ char TargetLowering::isNegatibleForFree(SDValue Op, SelectionDAG &DAG,
EVT VT = Op.getValueType();
const SDNodeFlags Flags = Op->getFlags();
const TargetOptions &Options = DAG.getTarget().Options;
- if (!Op.hasOneUse() && !(Op.getOpcode() == ISD::FP_EXTEND &&
- isFPExtFree(VT, Op.getOperand(0).getValueType())))
- return 0;
+ if (!Op.hasOneUse()) {
+ bool IsFreeExtend = Op.getOpcode() == ISD::FP_EXTEND &&
+ isFPExtFree(VT, Op.getOperand(0).getValueType());
+
+ // If we already have the use of the negated floating constant, it is free
+ // to negate it even it has multiple uses.
+ bool IsFreeConstant =
+ Op.getOpcode() == ISD::ConstantFP &&
+ !getNegatedExpression(Op, DAG, LegalOperations, ForCodeSize)
+ .use_empty();
+
+ if (!IsFreeExtend && !IsFreeConstant)
+ return 0;
+ }
// Don't recurse exponentially.
if (Depth > SelectionDAG::MaxRecursionDepth)
@@ -5687,14 +5698,7 @@ SDValue TargetLowering::getNegatedExpression(SDValue Op, SelectionDAG &DAG,
ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
- // TODO: This is a hack. It is possible that costs have changed between now
- // and the initial calls to isNegatibleForFree(). That is because we
- // are rewriting the expression, and that may change the number of
- // uses (and therefore the cost) of values. If the negation costs are
- // equal, only negate this value if it is a constant. Otherwise, try
- // operand 1. A better fix would eliminate uses as a cost factor or
- // track the change in uses as we rewrite the expression.
- if (V0 > V1 || (V0 == V1 && isa<ConstantFPSDNode>(Op.getOperand(0)))) {
+ if (V0 > V1) {
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
SDValue Neg0 = getNegatedExpression(
Op.getOperand(0), DAG, LegalOperations, ForCodeSize, Depth + 1);
diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index ea3e800a1ad2..651ad9ad4c83 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -211,6 +211,24 @@ AArch64FrameLowering::getStackIDForScalableVectors() const {
return TargetStackID::SVEVector;
}
+/// Returns the size of the fixed object area (allocated next to sp on entry)
+/// On Win64 this may include a var args area and an UnwindHelp object for EH.
+static unsigned getFixedObjectSize(const MachineFunction &MF,
+ const AArch64FunctionInfo *AFI, bool IsWin64,
+ bool IsFunclet) {
+ if (!IsWin64 || IsFunclet) {
+ // Only Win64 uses fixed objects, and then only for the function (not
+ // funclets)
+ return 0;
+ } else {
+ // Var args are stored here in the primary function.
+ const unsigned VarArgsArea = AFI->getVarArgsGPRSize();
+ // To support EH funclets we allocate an UnwindHelp object
+ const unsigned UnwindHelpObject = (MF.hasEHFunclets() ? 8 : 0);
+ return alignTo(VarArgsArea + UnwindHelpObject, 16);
+ }
+}
+
/// Returns the size of the entire SVE stackframe (calleesaves + spills).
static StackOffset getSVEStackSize(const MachineFunction &MF) {
const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
@@ -959,10 +977,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
- // Var args are accounted for in the containing function, so don't
- // include them for funclets.
- unsigned FixedObject = (IsWin64 && !IsFunclet) ?
- alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
+ unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject;
// All of the remaining stack allocations are for locals.
@@ -993,32 +1008,8 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
++MBBI;
}
- // The code below is not applicable to funclets. We have emitted all the SEH
- // opcodes that we needed to emit. The FP and BP belong to the containing
- // function.
- if (IsFunclet) {
- if (NeedsWinCFI) {
- HasWinCFI = true;
- BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd))
- .setMIFlag(MachineInstr::FrameSetup);
- }
-
- // SEH funclets are passed the frame pointer in X1. If the parent
- // function uses the base register, then the base register is used
- // directly, and is not retrieved from X1.
- if (F.hasPersonalityFn()) {
- EHPersonality Per = classifyEHPersonality(F.getPersonalityFn());
- if (isAsynchronousEHPersonality(Per)) {
- BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::COPY), AArch64::FP)
- .addReg(AArch64::X1).setMIFlag(MachineInstr::FrameSetup);
- MBB.addLiveIn(AArch64::X1);
- }
- }
-
- return;
- }
-
- if (HasFP) {
+ // For funclets the FP belongs to the containing function.
+ if (!IsFunclet && HasFP) {
// Only set up FP if we actually need to.
int64_t FPOffset = isTargetDarwin(MF) ? (AFI->getCalleeSavedStackSize() - 16) : 0;
@@ -1161,7 +1152,9 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
// Allocate space for the rest of the frame.
if (NumBytes) {
- const bool NeedsRealignment = RegInfo->needsStackRealignment(MF);
+ // Alignment is required for the parent frame, not the funclet
+ const bool NeedsRealignment =
+ !IsFunclet && RegInfo->needsStackRealignment(MF);
unsigned scratchSPReg = AArch64::SP;
if (NeedsRealignment) {
@@ -1215,7 +1208,8 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
// FIXME: Clarify FrameSetup flags here.
// Note: Use emitFrameOffset() like above for FP if the FrameSetup flag is
// needed.
- if (RegInfo->hasBasePointer(MF)) {
+ // For funclets the BP belongs to the containing function.
+ if (!IsFunclet && RegInfo->hasBasePointer(MF)) {
TII->copyPhysReg(MBB, MBBI, DL, RegInfo->getBaseRegister(), AArch64::SP,
false);
if (NeedsWinCFI) {
@@ -1232,6 +1226,19 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
.setMIFlag(MachineInstr::FrameSetup);
}
+ // SEH funclets are passed the frame pointer in X1. If the parent
+ // function uses the base register, then the base register is used
+ // directly, and is not retrieved from X1.
+ if (IsFunclet && F.hasPersonalityFn()) {
+ EHPersonality Per = classifyEHPersonality(F.getPersonalityFn());
+ if (isAsynchronousEHPersonality(Per)) {
+ BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::COPY), AArch64::FP)
+ .addReg(AArch64::X1)
+ .setMIFlag(MachineInstr::FrameSetup);
+ MBB.addLiveIn(AArch64::X1);
+ }
+ }
+
if (needsFrameMoves) {
const DataLayout &TD = MF.getDataLayout();
const int StackGrowth = isTargetDarwin(MF)
@@ -1450,10 +1457,7 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF,
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
- // Var args are accounted for in the containing function, so don't
- // include them for funclets.
- unsigned FixedObject =
- (IsWin64 && !IsFunclet) ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
+ unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
uint64_t AfterCSRPopSize = ArgumentPopSize;
auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject;
@@ -1679,7 +1683,9 @@ static StackOffset getFPOffset(const MachineFunction &MF, int64_t ObjectOffset)
const auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
- unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
+
+ unsigned FixedObject =
+ getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet=*/false);
unsigned FPAdjust = isTargetDarwin(MF)
? 16 : AFI->getCalleeSavedStackSize(MF.getFrameInfo());
return {ObjectOffset + FixedObject + FPAdjust, MVT::i8};
@@ -2632,9 +2638,14 @@ void AArch64FrameLowering::processFunctionBeforeFrameFinalized(
++MBBI;
// Create an UnwindHelp object.
- int UnwindHelpFI =
- MFI.CreateStackObject(/*size*/8, /*alignment*/16, false);
+ // The UnwindHelp object is allocated at the start of the fixed object area
+ int64_t FixedObject =
+ getFixedObjectSize(MF, AFI, /*IsWin64*/ true, /*IsFunclet*/ false);
+ int UnwindHelpFI = MFI.CreateFixedObject(/*Size*/ 8,
+ /*SPOffset*/ -FixedObject,
+ /*IsImmutable=*/false);
EHInfo.UnwindHelpFrameIdx = UnwindHelpFI;
+
// We need to store -2 into the UnwindHelp object at the start of the
// function.
DebugLoc DL;
@@ -2656,10 +2667,14 @@ int AArch64FrameLowering::getFrameIndexReferencePreferSP(
const MachineFunction &MF, int FI, unsigned &FrameReg,
bool IgnoreSPUpdates) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
- LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is "
- << MFI.getObjectOffset(FI) << "\n");
- FrameReg = AArch64::SP;
- return MFI.getObjectOffset(FI);
+ if (IgnoreSPUpdates) {
+ LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is "
+ << MFI.getObjectOffset(FI) << "\n");
+ FrameReg = AArch64::SP;
+ return MFI.getObjectOffset(FI);
+ }
+
+ return getFrameIndexReference(MF, FI, FrameReg);
}
/// The parent frame offset (aka dispFrame) is only used on X86_64 to retrieve
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 4311df5dbeb8..20c5ac7b378a 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -298,6 +298,11 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
switch (ExtraCode[0]) {
default: return true; // Unknown modifier.
+ case 'L': // A memory reference to the upper word of a double word op.
+ O << getDataLayout().getPointerSize() << "(";
+ printOperand(MI, OpNo, O);
+ O << ")";
+ return false;
case 'y': // A memory reference for an X-form instruction
{
const char *RegName = "r0";
@@ -309,7 +314,6 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
}
case 'U': // Print 'u' for update form.
case 'X': // Print 'x' for indexed form.
- {
// FIXME: Currently for PowerPC memory operands are always loaded
// into a register, so we never get an update or indexed form.
// This is bad even for offset forms, since even if we know we
@@ -319,7 +323,6 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
assert(MI->getOperand(OpNo).isReg());
return false;
}
- }
}
assert(MI->getOperand(OpNo).isReg());
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index c60fc3fc6b42..f7cd19cbb8e7 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -181,9 +181,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
adjustReg(MBB, MBBI, DL, FPReg, SPReg,
StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup);
- // Emit ".cfi_def_cfa $fp, 0"
+ // Emit ".cfi_def_cfa $fp, -RVFI->getVarArgsSaveSize()"
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfa(
- nullptr, RI->getDwarfRegNum(FPReg, true), 0));
+ nullptr, RI->getDwarfRegNum(FPReg, true), -RVFI->getVarArgsSaveSize()));
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 5a2cffbc824c..a1e3e326a97a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -197,6 +197,14 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setTruncStoreAction(MVT::f64, MVT::f16, Expand);
}
+ if (Subtarget.is64Bit() &&
+ !(Subtarget.hasStdExtD() || Subtarget.hasStdExtF())) {
+ setOperationAction(ISD::FP_TO_UINT, MVT::i32, Custom);
+ setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom);
+ setOperationAction(ISD::STRICT_FP_TO_UINT, MVT::i32, Custom);
+ setOperationAction(ISD::STRICT_FP_TO_SINT, MVT::i32, Custom);
+ }
+
setOperationAction(ISD::GlobalAddress, XLenVT, Custom);
setOperationAction(ISD::BlockAddress, XLenVT, Custom);
setOperationAction(ISD::ConstantPool, XLenVT, Custom);
@@ -876,6 +884,32 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
switch (N->getOpcode()) {
default:
llvm_unreachable("Don't know how to custom type legalize this operation!");
+ case ISD::STRICT_FP_TO_SINT:
+ case ISD::STRICT_FP_TO_UINT:
+ case ISD::FP_TO_SINT:
+ case ISD::FP_TO_UINT: {
+ bool IsStrict = N->isStrictFPOpcode();
+ assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+ "Unexpected custom legalisation");
+ SDValue Op0 = IsStrict ? N->getOperand(1) : N->getOperand(0);
+ RTLIB::Libcall LC;
+ if (N->getOpcode() == ISD::FP_TO_SINT ||
+ N->getOpcode() == ISD::STRICT_FP_TO_SINT)
+ LC = RTLIB::getFPTOSINT(Op0.getValueType(), N->getValueType(0));
+ else
+ LC = RTLIB::getFPTOUINT(Op0.getValueType(), N->getValueType(0));
+ MakeLibCallOptions CallOptions;
+ EVT OpVT = Op0.getValueType();
+ CallOptions.setTypeListBeforeSoften(OpVT, N->getValueType(0), true);
+ SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
+ SDValue Result;
+ std::tie(Result, Chain) =
+ makeLibCall(DAG, LC, N->getValueType(0), Op0, CallOptions, DL, Chain);
+ Results.push_back(Result);
+ if (IsStrict)
+ Results.push_back(Chain);
+ break;
+ }
case ISD::READCYCLECOUNTER: {
assert(!Subtarget.is64Bit() &&
"READCYCLECOUNTER only has custom type legalization on riscv32");
diff --git a/llvm/tools/llvm-dwp/DWPError.cpp b/llvm/tools/llvm-dwp/DWPError.cpp
new file mode 100644
index 000000000000..21d53ed6d198
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPError.cpp
@@ -0,0 +1,3 @@
+#include "DWPError.h"
+using namespace llvm;
+char DWPError::ID;
diff --git a/llvm/tools/llvm-dwp/DWPError.h b/llvm/tools/llvm-dwp/DWPError.h
new file mode 100644
index 000000000000..62025ed4caa5
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPError.h
@@ -0,0 +1,23 @@
+#ifndef TOOLS_LLVM_DWP_DWPERROR
+#define TOOLS_LLVM_DWP_DWPERROR
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <string>
+
+namespace llvm {
+class DWPError : public ErrorInfo<DWPError> {
+public:
+ DWPError(std::string Info) : Info(std::move(Info)) {}
+ void log(raw_ostream &OS) const override { OS << Info; }
+ std::error_code convertToErrorCode() const override {
+ llvm_unreachable("Not implemented");
+ }
+ static char ID;
+
+private:
+ std::string Info;
+};
+}
+
+#endif
diff --git a/llvm/tools/llvm-dwp/DWPStringPool.h b/llvm/tools/llvm-dwp/DWPStringPool.h
new file mode 100644
index 000000000000..7d41176b5619
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPStringPool.h
@@ -0,0 +1,56 @@
+#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL
+#define TOOLS_LLVM_DWP_DWPSTRINGPOOL
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCStreamer.h"
+#include <cassert>
+
+namespace llvm {
+class DWPStringPool {
+
+ struct CStrDenseMapInfo {
+ static inline const char *getEmptyKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0));
+ }
+ static inline const char *getTombstoneKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1));
+ }
+ static unsigned getHashValue(const char *Val) {
+ assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+ assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+ return (unsigned)hash_value(StringRef(Val));
+ }
+ static bool isEqual(const char *LHS, const char *RHS) {
+ if (RHS == getEmptyKey())
+ return LHS == getEmptyKey();
+ if (RHS == getTombstoneKey())
+ return LHS == getTombstoneKey();
+ return strcmp(LHS, RHS) == 0;
+ }
+ };
+
+ MCStreamer &Out;
+ MCSection *Sec;
+ DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool;
+ uint32_t Offset = 0;
+
+public:
+ DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {}
+
+ uint32_t getOffset(const char *Str, unsigned Length) {
+ assert(strlen(Str) + 1 == Length && "Ensure length hint is correct");
+
+ auto Pair = Pool.insert(std::make_pair(Str, Offset));
+ if (Pair.second) {
+ Out.SwitchSection(Sec);
+ Out.EmitBytes(StringRef(Str, Length));
+ Offset += Length;
+ }
+
+ return Pair.first->second;
+ }
+};
+}
+
+#endif
diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp
new file mode 100644
index 000000000000..23513ef8fb4e
--- /dev/null
+++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -0,0 +1,749 @@
+//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
+// package files).
+//
+//===----------------------------------------------------------------------===//
+#include "DWPError.h"
+#include "DWPStringPool.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
+#include "llvm/Object/Decompressor.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+cl::OptionCategory DwpCategory("Specific Options");
+static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(DwpCategory));
+
+static cl::list<std::string> ExecFilenames(
+ "e", cl::ZeroOrMore,
+ cl::desc("Specify the executable/library files to get the list of *.dwo from"),
+ cl::value_desc("filename"), cl::cat(DwpCategory));
+
+static cl::opt<std::string> OutputFilename(cl::Required, "o",
+ cl::desc("Specify the output file."),
+ cl::value_desc("filename"),
+ cl::cat(DwpCategory));
+
+static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings,
+ MCSection *StrOffsetSection,
+ StringRef CurStrSection,
+ StringRef CurStrOffsetSection) {
+ // Could possibly produce an error or warning if one of these was non-null but
+ // the other was null.
+ if (CurStrSection.empty() || CurStrOffsetSection.empty())
+ return;
+
+ DenseMap<uint64_t, uint32_t> OffsetRemapping;
+
+ DataExtractor Data(CurStrSection, true, 0);
+ uint64_t LocalOffset = 0;
+ uint64_t PrevOffset = 0;
+ while (const char *s = Data.getCStr(&LocalOffset)) {
+ OffsetRemapping[PrevOffset] =
+ Strings.getOffset(s, LocalOffset - PrevOffset);
+ PrevOffset = LocalOffset;
+ }
+
+ Data = DataExtractor(CurStrOffsetSection, true, 0);
+
+ Out.SwitchSection(StrOffsetSection);
+
+ uint64_t Offset = 0;
+ uint64_t Size = CurStrOffsetSection.size();
+ while (Offset < Size) {
+ auto OldOffset = Data.getU32(&Offset);
+ auto NewOffset = OffsetRemapping[OldOffset];
+ Out.EmitIntValue(NewOffset, 4);
+ }
+}
+
+static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
+ uint64_t CurCode;
+ uint64_t Offset = 0;
+ DataExtractor AbbrevData(Abbrev, true, 0);
+ while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) {
+ // Tag
+ AbbrevData.getULEB128(&Offset);
+ // DW_CHILDREN
+ AbbrevData.getU8(&Offset);
+ // Attributes
+ while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset))
+ ;
+ }
+ return Offset;
+}
+
+struct CompileUnitIdentifiers {
+ uint64_t Signature = 0;
+ const char *Name = "";
+ const char *DWOName = "";
+};
+
+static Expected<const char *>
+getIndexedString(dwarf::Form Form, DataExtractor InfoData,
+ uint64_t &InfoOffset, StringRef StrOffsets, StringRef Str) {
+ if (Form == dwarf::DW_FORM_string)
+ return InfoData.getCStr(&InfoOffset);
+ if (Form != dwarf::DW_FORM_GNU_str_index)
+ return make_error<DWPError>(
+ "string field encoded without DW_FORM_string or DW_FORM_GNU_str_index");
+ auto StrIndex = InfoData.getULEB128(&InfoOffset);
+ DataExtractor StrOffsetsData(StrOffsets, true, 0);
+ uint64_t StrOffsetsOffset = 4 * StrIndex;
+ uint64_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset);
+ DataExtractor StrData(Str, true, 0);
+ return StrData.getCStr(&StrOffset);
+}
+
+static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev,
+ StringRef Info,
+ StringRef StrOffsets,
+ StringRef Str) {
+ uint64_t Offset = 0;
+ DataExtractor InfoData(Info, true, 0);
+ dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32;
+ uint64_t Length = InfoData.getU32(&Offset);
+ // If the length is 0xffffffff, then this indictes that this is a DWARF 64
+ // stream and the length is actually encoded into a 64 bit value that follows.
+ if (Length == 0xffffffffU) {
+ Format = dwarf::DwarfFormat::DWARF64;
+ Length = InfoData.getU64(&Offset);
+ }
+ uint16_t Version = InfoData.getU16(&Offset);
+ InfoData.getU32(&Offset); // Abbrev offset (should be zero)
+ uint8_t AddrSize = InfoData.getU8(&Offset);
+
+ uint32_t AbbrCode = InfoData.getULEB128(&Offset);
+
+ DataExtractor AbbrevData(Abbrev, true, 0);
+ uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode);
+ auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset));
+ if (Tag != dwarf::DW_TAG_compile_unit)
+ return make_error<DWPError>("top level DIE is not a compile unit");
+ // DW_CHILDREN
+ AbbrevData.getU8(&AbbrevOffset);
+ uint32_t Name;
+ dwarf::Form Form;
+ CompileUnitIdentifiers ID;
+ Optional<uint64_t> Signature = None;
+ while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
+ (Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) &&
+ (Name != 0 || Form != 0)) {
+ switch (Name) {
+ case dwarf::DW_AT_name: {
+ Expected<const char *> EName =
+ getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
+ if (!EName)
+ return EName.takeError();
+ ID.Name = *EName;
+ break;
+ }
+ case dwarf::DW_AT_GNU_dwo_name: {
+ Expected<const char *> EName =
+ getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
+ if (!EName)
+ return EName.takeError();
+ ID.DWOName = *EName;
+ break;
+ }
+ case dwarf::DW_AT_GNU_dwo_id:
+ Signature = InfoData.getU64(&Offset);
+ break;
+ default:
+ DWARFFormValue::skipValue(Form, InfoData, &Offset,
+ dwarf::FormParams({Version, AddrSize, Format}));
+ }
+ }
+ if (!Signature)
+ return make_error<DWPError>("compile unit missing dwo_id");
+ ID.Signature = *Signature;
+ return ID;
+}
+
+struct UnitIndexEntry {
+ DWARFUnitIndex::Entry::SectionContribution Contributions[8];
+ std::string Name;
+ std::string DWOName;
+ StringRef DWPName;
+};
+
+static StringRef getSubsection(StringRef Section,
+ const DWARFUnitIndex::Entry &Entry,
+ DWARFSectionKind Kind) {
+ const auto *Off = Entry.getOffset(Kind);
+ if (!Off)
+ return StringRef();
+ return Section.substr(Off->Offset, Off->Length);
+}
+
+static void addAllTypesFromDWP(
+ MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+ const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types,
+ const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) {
+ Out.SwitchSection(OutputTypes);
+ for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) {
+ auto *I = E.getOffsets();
+ if (!I)
+ continue;
+ auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry));
+ if (!P.second)
+ continue;
+ auto &Entry = P.first->second;
+ // Zero out the debug_info contribution
+ Entry.Contributions[0] = {};
+ for (auto Kind : TUIndex.getColumnKinds()) {
+ auto &C = Entry.Contributions[Kind - DW_SECT_INFO];
+ C.Offset += I->Offset;
+ C.Length = I->Length;
+ ++I;
+ }
+ auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
+ Out.EmitBytes(Types.substr(
+ C.Offset - TUEntry.Contributions[DW_SECT_TYPES - DW_SECT_INFO].Offset,
+ C.Length));
+ C.Offset = TypesOffset;
+ TypesOffset += C.Length;
+ }
+}
+
+static void addAllTypes(MCStreamer &Out,
+ MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+ MCSection *OutputTypes,
+ const std::vector<StringRef> &TypesSections,
+ const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) {
+ for (StringRef Types : TypesSections) {
+ Out.SwitchSection(OutputTypes);
+ uint64_t Offset = 0;
+ DataExtractor Data(Types, true, 0);
+ while (Data.isValidOffset(Offset)) {
+ UnitIndexEntry Entry = CUEntry;
+ // Zero out the debug_info contribution
+ Entry.Contributions[0] = {};
+ auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
+ C.Offset = TypesOffset;
+ auto PrevOffset = Offset;
+ // Length of the unit, including the 4 byte length field.
+ C.Length = Data.getU32(&Offset) + 4;
+
+ Data.getU16(&Offset); // Version
+ Data.getU32(&Offset); // Abbrev offset
+ Data.getU8(&Offset); // Address size
+ auto Signature = Data.getU64(&Offset);
+ Offset = PrevOffset + C.Length;
+
+ auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry));
+ if (!P.second)
+ continue;
+
+ Out.EmitBytes(Types.substr(PrevOffset, C.Length));
+ TypesOffset += C.Length;
+ }
+ }
+}
+
+static void
+writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
+ const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
+ uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) {
+ for (const auto &E : IndexEntries)
+ for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i)
+ if (ContributionOffsets[i])
+ Out.EmitIntValue(E.second.Contributions[i].*Field, 4);
+}
+
+static void
+writeIndex(MCStreamer &Out, MCSection *Section,
+ ArrayRef<unsigned> ContributionOffsets,
+ const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) {
+ if (IndexEntries.empty())
+ return;
+
+ unsigned Columns = 0;
+ for (auto &C : ContributionOffsets)
+ if (C)
+ ++Columns;
+
+ std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2));
+ uint64_t Mask = Buckets.size() - 1;
+ size_t i = 0;
+ for (const auto &P : IndexEntries) {
+ auto S = P.first;
+ auto H = S & Mask;
+ auto HP = ((S >> 32) & Mask) | 1;
+ while (Buckets[H]) {
+ assert(S != IndexEntries.begin()[Buckets[H] - 1].first &&
+ "Duplicate unit");
+ H = (H + HP) & Mask;
+ }
+ Buckets[H] = i + 1;
+ ++i;
+ }
+
+ Out.SwitchSection(Section);
+ Out.EmitIntValue(2, 4); // Version
+ Out.EmitIntValue(Columns, 4); // Columns
+ Out.EmitIntValue(IndexEntries.size(), 4); // Num Units
+ Out.EmitIntValue(Buckets.size(), 4); // Num Buckets
+
+ // Write the signatures.
+ for (const auto &I : Buckets)
+ Out.EmitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8);
+
+ // Write the indexes.
+ for (const auto &I : Buckets)
+ Out.EmitIntValue(I, 4);
+
+ // Write the column headers (which sections will appear in the table)
+ for (size_t i = 0; i != ContributionOffsets.size(); ++i)
+ if (ContributionOffsets[i])
+ Out.EmitIntValue(i + DW_SECT_INFO, 4);
+
+ // Write the offsets.
+ writeIndexTable(Out, ContributionOffsets, IndexEntries,
+ &DWARFUnitIndex::Entry::SectionContribution::Offset);
+
+ // Write the lengths.
+ writeIndexTable(Out, ContributionOffsets, IndexEntries,
+ &DWARFUnitIndex::Entry::SectionContribution::Length);
+}
+
+std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWOName) {
+ std::string Text = "\'";
+ Text += Name;
+ Text += '\'';
+ if (!DWPName.empty()) {
+ Text += " (from ";
+ if (!DWOName.empty()) {
+ Text += '\'';
+ Text += DWOName;
+ Text += "' in ";
+ }
+ Text += '\'';
+ Text += DWPName;
+ Text += "')";
+ }
+ return Text;
+}
+
+static Error createError(StringRef Name, Error E) {
+ return make_error<DWPError>(
+ ("failure while decompressing compressed section: '" + Name + "', " +
+ llvm::toString(std::move(E)))
+ .str());
+}
+
+static Error
+handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections,
+ StringRef &Name, StringRef &Contents) {
+ if (!Decompressor::isGnuStyle(Name))
+ return Error::success();
+
+ Expected<Decompressor> Dec =
+ Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/);
+ if (!Dec)
+ return createError(Name, Dec.takeError());
+
+ UncompressedSections.emplace_back();
+ if (Error E = Dec->resizeAndDecompress(UncompressedSections.back()))
+ return createError(Name, std::move(E));
+
+ Name = Name.substr(2); // Drop ".z"
+ Contents = UncompressedSections.back();
+ return Error::success();
+}
+
+static Error handleSection(
+ const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections,
+ const MCSection *StrSection, const MCSection *StrOffsetSection,
+ const MCSection *TypesSection, const MCSection *CUIndexSection,
+ const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out,
+ std::deque<SmallString<32>> &UncompressedSections,
+ uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry,
+ StringRef &CurStrSection, StringRef &CurStrOffsetSection,
+ std::vector<StringRef> &CurTypesSection, StringRef &InfoSection,
+ StringRef &AbbrevSection, StringRef &CurCUIndexSection,
+ StringRef &CurTUIndexSection) {
+ if (Section.isBSS())
+ return Error::success();
+
+ if (Section.isVirtual())
+ return Error::success();
+
+ Expected<StringRef> NameOrErr = Section.getName();
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ StringRef Name = *NameOrErr;
+
+ Expected<StringRef> ContentsOrErr = Section.getContents();
+ if (!ContentsOrErr)
+ return ContentsOrErr.takeError();
+ StringRef Contents = *ContentsOrErr;
+
+ if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents))
+ return Err;
+
+ Name = Name.substr(Name.find_first_not_of("._"));
+
+ auto SectionPair = KnownSections.find(Name);
+ if (SectionPair == KnownSections.end())
+ return Error::success();
+
+ if (DWARFSectionKind Kind = SectionPair->second.second) {
+ auto Index = Kind - DW_SECT_INFO;
+ if (Kind != DW_SECT_TYPES) {
+ CurEntry.Contributions[Index].Offset = ContributionOffsets[Index];
+ ContributionOffsets[Index] +=
+ (CurEntry.Contributions[Index].Length = Contents.size());
+ }
+
+ switch (Kind) {
+ case DW_SECT_INFO:
+ InfoSection = Contents;
+ break;
+ case DW_SECT_ABBREV:
+ AbbrevSection = Contents;
+ break;
+ default:
+ break;
+ }
+ }
+
+ MCSection *OutSection = SectionPair->second.first;
+ if (OutSection == StrOffsetSection)
+ CurStrOffsetSection = Contents;
+ else if (OutSection == StrSection)
+ CurStrSection = Contents;
+ else if (OutSection == TypesSection)
+ CurTypesSection.push_back(Contents);
+ else if (OutSection == CUIndexSection)
+ CurCUIndexSection = Contents;
+ else if (OutSection == TUIndexSection)
+ CurTUIndexSection = Contents;
+ else {
+ Out.SwitchSection(OutSection);
+ Out.EmitBytes(Contents);
+ }
+ return Error::success();
+}
+
+static Error
+buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
+ const CompileUnitIdentifiers &ID, StringRef DWPName) {
+ return make_error<DWPError>(
+ std::string("Duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " +
+ buildDWODescription(PrevE.second.Name, PrevE.second.DWPName,
+ PrevE.second.DWOName) +
+ " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName));
+}
+
+static Expected<SmallVector<std::string, 16>>
+getDWOFilenames(StringRef ExecFilename) {
+ auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename);
+ if (!ErrOrObj)
+ return ErrOrObj.takeError();
+
+ const ObjectFile &Obj = *ErrOrObj.get().getBinary();
+ std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
+
+ SmallVector<std::string, 16> DWOPaths;
+ for (const auto &CU : DWARFCtx->compile_units()) {
+ const DWARFDie &Die = CU->getUnitDIE();
+ std::string DWOName = dwarf::toString(
+ Die.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
+ if (DWOName.empty())
+ continue;
+ std::string DWOCompDir =
+ dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), "");
+ if (!DWOCompDir.empty()) {
+ SmallString<16> DWOPath;
+ sys::path::append(DWOPath, DWOCompDir, DWOName);
+ DWOPaths.emplace_back(DWOPath.data(), DWOPath.size());
+ } else {
+ DWOPaths.push_back(std::move(DWOName));
+ }
+ }
+ return std::move(DWOPaths);
+}
+
+static Error write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
+ const auto &MCOFI = *Out.getContext().getObjectFileInfo();
+ MCSection *const StrSection = MCOFI.getDwarfStrDWOSection();
+ MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection();
+ MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection();
+ MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection();
+ MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection();
+ const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = {
+ {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}},
+ {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_TYPES}},
+ {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}},
+ {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}},
+ {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_LOC}},
+ {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}},
+ {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
+ {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}},
+ {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}};
+
+ MapVector<uint64_t, UnitIndexEntry> IndexEntries;
+ MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries;
+
+ uint32_t ContributionOffsets[8] = {};
+
+ DWPStringPool Strings(Out, StrSection);
+
+ SmallVector<OwningBinary<object::ObjectFile>, 128> Objects;
+ Objects.reserve(Inputs.size());
+
+ std::deque<SmallString<32>> UncompressedSections;
+
+ for (const auto &Input : Inputs) {
+ auto ErrOrObj = object::ObjectFile::createObjectFile(Input);
+ if (!ErrOrObj)
+ return ErrOrObj.takeError();
+
+ auto &Obj = *ErrOrObj->getBinary();
+ Objects.push_back(std::move(*ErrOrObj));
+
+ UnitIndexEntry CurEntry = {};
+
+ StringRef CurStrSection;
+ StringRef CurStrOffsetSection;
+ std::vector<StringRef> CurTypesSection;
+ StringRef InfoSection;
+ StringRef AbbrevSection;
+ StringRef CurCUIndexSection;
+ StringRef CurTUIndexSection;
+
+ for (const auto &Section : Obj.sections())
+ if (auto Err = handleSection(
+ KnownSections, StrSection, StrOffsetSection, TypesSection,
+ CUIndexSection, TUIndexSection, Section, Out,
+ UncompressedSections, ContributionOffsets, CurEntry,
+ CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection,
+ AbbrevSection, CurCUIndexSection, CurTUIndexSection))
+ return Err;
+
+ if (InfoSection.empty())
+ continue;
+
+ writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection,
+ CurStrOffsetSection);
+
+ if (CurCUIndexSection.empty()) {
+ Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
+ AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection);
+ if (!EID)
+ return createFileError(Input, EID.takeError());
+ const auto &ID = *EID;
+ auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry));
+ if (!P.second)
+ return buildDuplicateError(*P.first, ID, "");
+ P.first->second.Name = ID.Name;
+ P.first->second.DWOName = ID.DWOName;
+ addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection,
+ CurEntry, ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
+ continue;
+ }
+
+ DWARFUnitIndex CUIndex(DW_SECT_INFO);
+ DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0);
+ if (!CUIndex.parse(CUIndexData))
+ return make_error<DWPError>("Failed to parse cu_index");
+
+ for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) {
+ auto *I = E.getOffsets();
+ if (!I)
+ continue;
+ auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry));
+ Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
+ getSubsection(AbbrevSection, E, DW_SECT_ABBREV),
+ getSubsection(InfoSection, E, DW_SECT_INFO),
+ getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS),
+ CurStrSection);
+ if (!EID)
+ return createFileError(Input, EID.takeError());
+ const auto &ID = *EID;
+ if (!P.second)
+ return buildDuplicateError(*P.first, ID, Input);
+ auto &NewEntry = P.first->second;
+ NewEntry.Name = ID.Name;
+ NewEntry.DWOName = ID.DWOName;
+ NewEntry.DWPName = Input;
+ for (auto Kind : CUIndex.getColumnKinds()) {
+ auto &C = NewEntry.Contributions[Kind - DW_SECT_INFO];
+ C.Offset += I->Offset;
+ C.Length = I->Length;
+ ++I;
+ }
+ }
+
+ if (!CurTypesSection.empty()) {
+ if (CurTypesSection.size() != 1)
+ return make_error<DWPError>("multiple type unit sections in .dwp file");
+ DWARFUnitIndex TUIndex(DW_SECT_TYPES);
+ DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0);
+ if (!TUIndex.parse(TUIndexData))
+ return make_error<DWPError>("Failed to parse tu_index");
+ addAllTypesFromDWP(Out, TypeIndexEntries, TUIndex, TypesSection,
+ CurTypesSection.front(), CurEntry,
+ ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
+ }
+ }
+
+ // Lie about there being no info contributions so the TU index only includes
+ // the type unit contribution
+ ContributionOffsets[0] = 0;
+ writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets,
+ TypeIndexEntries);
+
+ // Lie about the type contribution
+ ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO] = 0;
+ // Unlie about the info contribution
+ ContributionOffsets[0] = 1;
+
+ writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
+ IndexEntries);
+
+ return Error::success();
+}
+
+static int error(const Twine &Error, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("error: ") + Error + "\n";
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+
+ std::string ErrorStr;
+ StringRef Context = "dwarf streamer init";
+
+ Triple TheTriple("x86_64-linux-gnu");
+
+ // Get the target.
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
+ if (!TheTarget)
+ return error(ErrorStr, Context);
+ std::string TripleName = TheTriple.getTriple();
+
+ // Create all the MC Objects.
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ return error(Twine("no register info for target ") + TripleName, Context);
+
+ MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ return error("no asm info for target " + TripleName, Context);
+
+ MCObjectFileInfo MOFI;
+ MCContext MC(MAI.get(), MRI.get(), &MOFI);
+ MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, MC);
+
+ std::unique_ptr<MCSubtargetInfo> MSTI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return error("no subtarget info for target " + TripleName, Context);
+
+ MCTargetOptions Options;
+ auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options);
+ if (!MAB)
+ return error("no asm backend for target " + TripleName, Context);
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ return error("no instr info info for target " + TripleName, Context);
+
+ MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC);
+ if (!MCE)
+ return error("no code emitter for target " + TripleName, Context);
+
+ // Create the output file.
+ std::error_code EC;
+ ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
+ Optional<buffer_ostream> BOS;
+ raw_pwrite_stream *OS;
+ if (EC)
+ return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+ if (OutFile.os().supportsSeeking()) {
+ OS = &OutFile.os();
+ } else {
+ BOS.emplace(OutFile.os());
+ OS = BOS.getPointer();
+ }
+
+ std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
+ TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI,
+ MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ if (!MS)
+ return error("no object streamer for target " + TripleName, Context);
+
+ std::vector<std::string> DWOFilenames = InputFiles;
+ for (const auto &ExecFilename : ExecFilenames) {
+ auto DWOs = getDWOFilenames(ExecFilename);
+ if (!DWOs) {
+ logAllUnhandledErrors(DWOs.takeError(), WithColor::error());
+ return 1;
+ }
+ DWOFilenames.insert(DWOFilenames.end(),
+ std::make_move_iterator(DWOs->begin()),
+ std::make_move_iterator(DWOs->end()));
+ }
+
+ if (auto Err = write(*MS, DWOFilenames)) {
+ logAllUnhandledErrors(std::move(Err), WithColor::error());
+ return 1;
+ }
+
+ MS->Finish();
+ OutFile.keep();
+ return 0;
+}
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
new file mode 100644
index 000000000000..46ece5a6f0c9
--- /dev/null
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -0,0 +1,894 @@
+//===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like traditional Unix "size",
+// that is, it prints out the size of each section, and the total size of all
+// sections.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace object;
+
+cl::OptionCategory SizeCat("llvm-size Options");
+
+enum OutputFormatTy { berkeley, sysv, darwin };
+static cl::opt<OutputFormatTy>
+ OutputFormat("format", cl::desc("Specify output format"),
+ cl::values(clEnumVal(sysv, "System V format"),
+ clEnumVal(berkeley, "Berkeley format"),
+ clEnumVal(darwin, "Darwin -m format")),
+ cl::init(berkeley), cl::cat(SizeCat));
+
+static cl::opt<OutputFormatTy>
+ OutputFormatShort(cl::desc("Specify output format"),
+ cl::values(clEnumValN(sysv, "A", "System V format"),
+ clEnumValN(berkeley, "B", "Berkeley format"),
+ clEnumValN(darwin, "m", "Darwin -m format")),
+ cl::init(berkeley), cl::cat(SizeCat));
+
+static bool BerkeleyHeaderPrinted = false;
+static bool MoreThanOneFile = false;
+static uint64_t TotalObjectText = 0;
+static uint64_t TotalObjectData = 0;
+static uint64_t TotalObjectBss = 0;
+static uint64_t TotalObjectTotal = 0;
+
+cl::opt<bool>
+ DarwinLongFormat("l",
+ cl::desc("When format is darwin, use long format "
+ "to include addresses and offsets."),
+ cl::cat(SizeCat));
+
+cl::opt<bool>
+ ELFCommons("common",
+ cl::desc("Print common symbols in the ELF file. When using "
+ "Berkeley format, this is added to bss."),
+ cl::init(false), cl::cat(SizeCat));
+
+static cl::list<std::string>
+ ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
+ cl::ZeroOrMore, cl::cat(SizeCat));
+static bool ArchAll = false;
+
+enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
+static cl::opt<RadixTy> Radix(
+ "radix", cl::desc("Print size in radix"), cl::init(decimal),
+ cl::values(clEnumValN(octal, "8", "Print size in octal"),
+ clEnumValN(decimal, "10", "Print size in decimal"),
+ clEnumValN(hexadecimal, "16", "Print size in hexadecimal")),
+ cl::cat(SizeCat));
+
+static cl::opt<RadixTy> RadixShort(
+ cl::desc("Print size in radix:"),
+ cl::values(clEnumValN(octal, "o", "Print size in octal"),
+ clEnumValN(decimal, "d", "Print size in decimal"),
+ clEnumValN(hexadecimal, "x", "Print size in hexadecimal")),
+ cl::init(decimal), cl::cat(SizeCat));
+
+static cl::opt<bool>
+ TotalSizes("totals",
+ cl::desc("Print totals of all objects - Berkeley format only"),
+ cl::init(false), cl::cat(SizeCat));
+
+static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"),
+ cl::aliasopt(TotalSizes));
+
+static cl::list<std::string>
+ InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
+
+static cl::extrahelp
+ HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+
+static bool HadError = false;
+
+static std::string ToolName;
+
+static void error(const Twine &Message, StringRef File) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
+}
+
+// This version of error() prints the archive name and member name, for example:
+// "libx.a(foo.o)" after the ToolName before the error message. It sets
+// HadError but returns allowing the code to move on to other archive members.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << FileName << "'";
+
+ Expected<StringRef> NameOrErr = C.getName();
+ // TODO: if we have a error getting the name then it would be nice to print
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ errs() << "(" << "???" << ")";
+ } else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+// This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName
+// before the error message. It sets HadError but returns allowing the code to
+// move on to other architecture slices.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << FileName << "'";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+/// Get the length of the string that represents @p num in Radix including the
+/// leading 0x or 0 for hexadecimal and octal respectively.
+static size_t getNumLengthAsString(uint64_t num) {
+ APInt conv(64, num);
+ SmallString<32> result;
+ conv.toString(result, Radix, false, true);
+ return result.size();
+}
+
+/// Return the printing format for the Radix.
+static const char *getRadixFmt() {
+ switch (Radix) {
+ case octal:
+ return PRIo64;
+ case decimal:
+ return PRIu64;
+ case hexadecimal:
+ return PRIx64;
+ }
+ return nullptr;
+}
+
+/// Remove unneeded ELF sections from calculation
+static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
+ if (!Obj->isELF())
+ return true;
+ switch (static_cast<ELFSectionRef>(Section).getType()) {
+ case ELF::SHT_NULL:
+ case ELF::SHT_SYMTAB:
+ case ELF::SHT_STRTAB:
+ case ELF::SHT_REL:
+ case ELF::SHT_RELA:
+ return false;
+ }
+ return true;
+}
+
+/// Total size of all ELF common symbols
+static uint64_t getCommonSize(ObjectFile *Obj) {
+ uint64_t TotalCommons = 0;
+ for (auto &Sym : Obj->symbols())
+ if (Obj->getSymbolFlags(Sym.getRawDataRefImpl()) & SymbolRef::SF_Common)
+ TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
+ return TotalCommons;
+}
+
+/// Print the size of each Mach-O segment and section in @p MachO.
+///
+/// This is when used when @c OutputFormat is darwin and produces the same
+/// output as darwin's size(1) -m output.
+static void printDarwinSectionSizes(MachOObjectFile *MachO) {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ if (Radix == hexadecimal)
+ fmt << "0x";
+ fmt << "%" << radix_fmt;
+
+ uint32_t Filetype = MachO->getHeader().filetype;
+
+ uint64_t total = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg.vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ outs() << format(fmt.str().c_str(), Sec.size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ uint64_t Seg_vmsize = Seg.vmsize;
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg_vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ uint64_t Sec_size = Sec.size;
+ outs() << format(fmt.str().c_str(), Sec_size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ }
+ }
+ outs() << "total " << format(fmt.str().c_str(), total) << "\n";
+}
+
+/// Print the summary sizes of the standard Mach-O segments in @p MachO.
+///
+/// This is when used when @c OutputFormat is berkeley with a Mach-O file and
+/// produces the same output as darwin's size(1) default output.
+static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_objc = 0;
+ uint64_t total_others = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ }
+ }
+ uint64_t total = total_text + total_data + total_objc + total_others;
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
+ BerkeleyHeaderPrinted = true;
+ }
+ outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
+ << total_others << "\t" << total << "\t" << format("%" PRIx64, total)
+ << "\t";
+}
+
+/// Print the size of each section in @p Obj.
+///
+/// The format used is determined by @c OutputFormat and @c Radix.
+static void printObjectSectionSizes(ObjectFile *Obj) {
+ uint64_t total = 0;
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+
+ // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's
+ // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object
+ // let it fall through to OutputFormat berkeley.
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
+ if (OutputFormat == darwin && MachO)
+ printDarwinSectionSizes(MachO);
+ // If we have a MachOObjectFile and the OutputFormat is berkeley print as
+ // darwin's default berkeley format for Mach-O files.
+ else if (MachO && OutputFormat == berkeley)
+ printDarwinSegmentSizes(MachO);
+ else if (OutputFormat == sysv) {
+ // Run two passes over all sections. The first gets the lengths needed for
+ // formatting the output. The second actually does the output.
+ std::size_t max_name_len = strlen("section");
+ std::size_t max_size_len = strlen("size");
+ std::size_t max_addr_len = strlen("addr");
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+ uint64_t size = Section.getSize();
+ total += size;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t addr = Section.getAddress();
+ max_name_len = std::max(max_name_len, name_or_err->size());
+ max_size_len = std::max(max_size_len, getNumLengthAsString(size));
+ max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
+ }
+
+ // Add extra padding.
+ max_name_len += 2;
+ max_size_len += 2;
+ max_addr_len += 2;
+
+ // Setup header format.
+ fmt << "%-" << max_name_len << "s "
+ << "%" << max_size_len << "s "
+ << "%" << max_addr_len << "s\n";
+
+ // Print header
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("section"),
+ static_cast<const char *>("size"),
+ static_cast<const char *>("addr"));
+ fmtbuf.clear();
+
+ // Setup per section format.
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << " "
+ << "%#" << max_addr_len << radix_fmt << "\n";
+
+ // Print each section.
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t size = Section.getSize();
+ uint64_t addr = Section.getAddress();
+ outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr);
+ }
+
+ if (ELFCommons) {
+ uint64_t CommonSize = getCommonSize(Obj);
+ total += CommonSize;
+ outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
+ CommonSize, static_cast<uint64_t>(0));
+ }
+
+ // Print total.
+ fmtbuf.clear();
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << "\n";
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"),
+ total)
+ << "\n\n";
+ } else {
+ // The Berkeley format does not display individual section sizes. It
+ // displays the cumulative size for each section type.
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_bss = 0;
+
+ // Make one pass over the section table to calculate sizes.
+ for (const SectionRef &Section : Obj->sections()) {
+ uint64_t size = Section.getSize();
+ bool isText = Section.isBerkeleyText();
+ bool isData = Section.isBerkeleyData();
+ bool isBSS = Section.isBSS();
+ if (isText)
+ total_text += size;
+ else if (isData)
+ total_data += size;
+ else if (isBSS)
+ total_bss += size;
+ }
+
+ if (ELFCommons)
+ total_bss += getCommonSize(Obj);
+
+ total = total_text + total_data + total_bss;
+
+ if (TotalSizes) {
+ TotalObjectText += total_text;
+ TotalObjectData += total_data;
+ TotalObjectBss += total_bss;
+ TotalObjectTotal += total;
+ }
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << " text\t"
+ " data\t"
+ " bss\t"
+ " "
+ << (Radix == octal ? "oct" : "dec")
+ << "\t"
+ " hex\t"
+ "filename\n";
+ BerkeleyHeaderPrinted = true;
+ }
+
+ // Print result.
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
+ outs() << format(fmt.str().c_str(), total_text, total_data, total_bss);
+ fmtbuf.clear();
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), total, total);
+ }
+}
+
+/// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there
+/// is a list of architecture flags specified then check to make sure this
+/// Mach-O file is one of those architectures or all architectures was
+/// specificed. If not then an error is generated and this routine returns
+/// false. Else it returns true.
+static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
+ auto *MachO = dyn_cast<MachOObjectFile>(O);
+
+ if (!MachO || ArchAll || ArchFlags.empty())
+ return true;
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ Triple T;
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
+ }
+ if (none_of(ArchFlags, [&](const std::string &Name) {
+ return Name == T.getArchName();
+ })) {
+ error("no architecture specified", Filename);
+ return false;
+ }
+ return true;
+}
+
+/// Print the section sizes for @p file. If @p file is an archive, print the
+/// section sizes for each archive member.
+static void printFileSectionSizes(StringRef file) {
+
+ // Attempt to open the binary.
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
+ if (!BinaryOrErr) {
+ error(BinaryOrErr.takeError(), file);
+ return;
+ }
+ Binary &Bin = *BinaryOrErr.get().getBinary();
+
+ if (Archive *a = dyn_cast<Archive>(&Bin)) {
+ // This is an archive. Iterate over each member and display its sizes.
+ Error Err = Error::success();
+ for (auto &C : a->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ error(std::move(E), a->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), a->getFileName());
+ } else if (MachOUniversalBinary *UB =
+ dyn_cast<MachOUniversalBinary>(&Bin)) {
+ // If we have a list of architecture flags specified dump only those.
+ if (!ArchAll && !ArchFlags.empty()) {
+ // Look for a slice in the universal binary that matches each ArchFlag.
+ bool ArchFound;
+ for (unsigned i = 0; i < ArchFlags.size(); ++i) {
+ ArchFound = false;
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (ArchFlags[i] == I->getArchFlagName()) {
+ ArchFound = true;
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "): \n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ UO.takeError())) {
+ error(std::move(E), file, ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C,
+ ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO) {
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")";
+ if (ArchFlags.size() > 1)
+ outs() << " (for architecture " << I->getArchFlagName()
+ << ")";
+ outs() << "\n";
+ } else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ }
+ if (!ArchFound) {
+ error("file does not contain architecture " + ArchFlags[i], file);
+ return;
+ }
+ }
+ return;
+ }
+ // No architecture flags were specified so if this contains a slice that
+ // matches the host architecture dump only that.
+ if (!ArchAll) {
+ StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (HostArchName == I->getArchFlagName()) {
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ return;
+ }
+ }
+ }
+ // Either all architectures have been specified or none have been specified
+ // and this does not contain the host architecture so dump all the slices.
+ bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):";
+ outs() << "\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin && MoreThanOneFile)
+ outs() << o->getFileName() << ":\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName();
+ outs() << "\n";
+ }
+ } else {
+ error("unsupported file type", file);
+ }
+}
+
+static void printBerkeleyTotals() {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
+ outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData,
+ TotalObjectBss);
+ fmtbuf.clear();
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
+ << "(TOTALS)\n";
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions(SizeCat);
+ cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
+
+ ToolName = argv[0];
+ if (OutputFormatShort.getNumOccurrences())
+ OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort);
+ if (RadixShort.getNumOccurrences())
+ Radix = RadixShort.getValue();
+
+ for (StringRef Arch : ArchFlags) {
+ if (Arch == "all") {
+ ArchAll = true;
+ } else {
+ if (!MachOObjectFile::isValidArch(Arch)) {
+ outs() << ToolName << ": for the -arch option: Unknown architecture "
+ << "named '" << Arch << "'";
+ return 1;
+ }
+ }
+ }
+
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+
+ MoreThanOneFile = InputFilenames.size() > 1;
+ llvm::for_each(InputFilenames, printFileSectionSizes);
+ if (OutputFormat == berkeley && TotalSizes)
+ printBerkeleyTotals();
+
+ if (HadError)
+ return 1;
+}
diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp
new file mode 100644
index 000000000000..51313d73401e
--- /dev/null
+++ b/llvm/tools/llvm-strings/llvm-strings.cpp
@@ -0,0 +1,120 @@
+//===-- llvm-strings.cpp - Printable String dumping utility ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like binutils "strings", that is, it
+// prints out printable strings in a binary, objdump, or archive file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/Binary.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Program.h"
+#include <cctype>
+#include <string>
+
+using namespace llvm;
+using namespace llvm::object;
+
+static cl::list<std::string> InputFileNames(cl::Positional,
+ cl::desc("<input object files>"),
+ cl::ZeroOrMore);
+
+static cl::opt<bool>
+ PrintFileName("print-file-name",
+ cl::desc("Print the name of the file before each string"));
+static cl::alias PrintFileNameShort("f", cl::desc(""),
+ cl::aliasopt(PrintFileName));
+
+static cl::opt<int>
+ MinLength("bytes", cl::desc("Print sequences of the specified length"),
+ cl::init(4));
+static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength));
+
+static cl::opt<bool>
+ AllSections("all",
+ cl::desc("Check all sections, not just the data section"));
+static cl::alias AllSectionsShort("a", cl::desc(""),
+ cl::aliasopt(AllSections));
+
+enum radix { none, octal, hexadecimal, decimal };
+static cl::opt<radix>
+ Radix("radix", cl::desc("print the offset within the file"),
+ cl::values(clEnumValN(octal, "o", "octal"),
+ clEnumValN(hexadecimal, "x", "hexadecimal"),
+ clEnumValN(decimal, "d", "decimal")),
+ cl::init(none));
+static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix));
+
+static cl::extrahelp
+ HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+
+static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
+ auto print = [&OS, FileName](unsigned Offset, StringRef L) {
+ if (L.size() < static_cast<size_t>(MinLength))
+ return;
+ if (PrintFileName)
+ OS << FileName << ": ";
+ switch (Radix) {
+ case none:
+ break;
+ case octal:
+ OS << format("%7o ", Offset);
+ break;
+ case hexadecimal:
+ OS << format("%7x ", Offset);
+ break;
+ case decimal:
+ OS << format("%7u ", Offset);
+ break;
+ }
+ OS << L << '\n';
+ };
+
+ const char *B = Contents.begin();
+ const char *P = nullptr, *E = nullptr, *S = nullptr;
+ for (P = Contents.begin(), E = Contents.end(); P < E; ++P) {
+ if (isPrint(*P) || *P == '\t') {
+ if (S == nullptr)
+ S = P;
+ } else if (S) {
+ print(S - B, StringRef(S, P - S));
+ S = nullptr;
+ }
+ }
+ if (S)
+ print(S - B, StringRef(S, E - S));
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n");
+ if (MinLength == 0) {
+ errs() << "invalid minimum string length 0\n";
+ return EXIT_FAILURE;
+ }
+
+ if (InputFileNames.empty())
+ InputFileNames.push_back("-");
+
+ for (const auto &File : InputFileNames) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ MemoryBuffer::getFileOrSTDIN(File);
+ if (std::error_code EC = Buffer.getError())
+ errs() << File << ": " << EC.message() << '\n';
+ else
+ strings(llvm::outs(), File == "-" ? "{standard input}" : File,
+ Buffer.get()->getMemBufferRef().getBuffer());
+ }
+
+ return EXIT_SUCCESS;
+}