diff options
author | Chris Lattner <sabre@nondot.org> | 2009-08-12 07:22:17 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2009-08-12 07:22:17 +0000 |
commit | a7ac47cee1a0b3f4c798ecaa22ecf9d1be9c07e6 (patch) | |
tree | ac43fb9b39f3a7abe8aa7cb217860e4d29329963 | |
parent | 54d26fa799e2a43793b94afdf077fab2412e46ea (diff) |
Change TargetAsmInfo to be constructed via TargetRegistry from a Target+Triple
pair instead of from a virtual method on TargetMachine. This cuts the final
ties of TargetAsmInfo to TargetMachine, meaning that MC can now use
TargetAsmInfo.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@78802 91177308-0d34-0410-b5e6-96231b3b80d8
50 files changed, 209 insertions, 167 deletions
diff --git a/include/llvm/Target/TargetMachine.h b/include/llvm/Target/TargetMachine.h index 5328a49234..e5ea27fb08 100644 --- a/include/llvm/Target/TargetMachine.h +++ b/include/llvm/Target/TargetMachine.h @@ -102,12 +102,8 @@ protected: // Can only create subclasses. /// AsmInfo - Contains target specific asm information. /// - mutable const TargetAsmInfo *AsmInfo; + const TargetAsmInfo *AsmInfo; - /// createTargetAsmInfo - Create a new instance of target specific asm - /// information. - virtual const TargetAsmInfo *createTargetAsmInfo() const { return 0; } - public: virtual ~TargetMachine(); @@ -126,10 +122,7 @@ public: /// getTargetAsmInfo - Return target specific asm information. /// - const TargetAsmInfo *getTargetAsmInfo() const { - if (!AsmInfo) AsmInfo = createTargetAsmInfo(); - return AsmInfo; - } + const TargetAsmInfo *getTargetAsmInfo() const { return AsmInfo; } /// getSubtarget - This method returns a pointer to the specified type of /// TargetSubtarget. In debug builds, it verifies that the object being @@ -291,9 +284,8 @@ public: /// class LLVMTargetMachine : public TargetMachine { protected: // Can only create subclasses. - LLVMTargetMachine(const Target &T, const std::string &TargetTriple) - : TargetMachine(T) { } - + LLVMTargetMachine(const Target &T, const std::string &TargetTriple); + /// addCommonCodeGenPasses - Add standard LLVM codegen passes used for /// both emitting to assembly files or machine code output. /// diff --git a/include/llvm/Target/TargetRegistry.h b/include/llvm/Target/TargetRegistry.h index ea95222d3a..87be7f39e5 100644 --- a/include/llvm/Target/TargetRegistry.h +++ b/include/llvm/Target/TargetRegistry.h @@ -43,20 +43,22 @@ namespace llvm { /// will be zero initialized), and pass that instance to the TargetRegistry as /// part of their initialization. class Target { - private: + public: + friend struct TargetRegistry; + typedef unsigned (*TripleMatchQualityFnTy)(const std::string &TT); - typedef TargetMachine *(*TargetMachineCtorTy)(const Target &, - const std::string &, - const std::string &); + typedef const TargetAsmInfo *(*AsmInfoCtorFnTy)(const Target &T, + const StringRef &TT); + typedef TargetMachine *(*TargetMachineCtorTy)(const Target &T, + const std::string &TT, + const std::string &Features); typedef FunctionPass *(*AsmPrinterCtorTy)(formatted_raw_ostream &, TargetMachine &, bool); typedef TargetAsmParser *(*AsmParserCtorTy)(const Target &, MCAsmParser &); - - friend struct TargetRegistry; - + private: /// Next - The next registered target in the linked list, maintained by the /// TargetRegistry. Target *Next; @@ -74,6 +76,8 @@ namespace llvm { /// HasJIT - Whether this target supports the JIT. bool HasJIT; + AsmInfoCtorFnTy AsmInfoCtorFn; + /// TargetMachineCtorFn - Construction function for this target's /// TargetMachine, if registered. TargetMachineCtorTy TargetMachineCtorFn; @@ -107,11 +111,23 @@ namespace llvm { /// hasAsmParser - Check if this target supports .s parsing. bool hasAsmParser() const { return AsmParserCtorFn != 0; } + + /// createAsmInfo - Create a TargetAsmInfo implementation for the specified + /// target triple. + /// + /// \arg Triple - This argument is used to determine the target machine + /// feature set; it should always be provided. Generally this should be + /// either the target triple from the module, or the target triple of the + /// host if that does not exist. + const TargetAsmInfo *createAsmInfo(const StringRef &Triple) const { + if (!AsmInfoCtorFn) + return 0; + return AsmInfoCtorFn(*this, Triple); + } + /// createTargetMachine - Create a target specific machine implementation - /// for the module \arg M and \arg Triple. + /// for the specified \arg Triple. /// - /// \arg M - This argument is used for some machines to access the target - /// data. /// \arg Triple - This argument is used to determine the target machine /// feature set; it should always be provided. Generally this should be /// either the target triple from the module, or the target triple of the @@ -228,7 +244,22 @@ namespace llvm { const char *ShortDesc, Target::TripleMatchQualityFnTy TQualityFn, bool HasJIT = false); - + + /// RegisterAsmInfo - Register a TargetAsmInfo implementation for the + /// given target. + /// + /// Clients are responsible for ensuring that registration doesn't occur + /// while another thread is attempting to access the registry. Typically + /// this is done by initializing all targets at program startup. + /// + /// @param T - The target being registered. + /// @param Fn - A function to construct a TargetAsmInfo for the target. + static void RegisterAsmInfo(Target &T, Target::AsmInfoCtorFnTy Fn) { + // Ignore duplicate registration. + if (!T.AsmInfoCtorFn) + T.AsmInfoCtorFn = Fn; + } + /// RegisterTargetMachine - Register a TargetMachine implementation for the /// given target. /// @@ -305,6 +336,41 @@ namespace llvm { } }; + /// RegisterAsmInfo - Helper template for registering a target assembly info + /// implementation. This invokes the static "Create" method on the class to + /// actually do the construction. Usage: + /// + /// extern "C" void LLVMInitializeFooTarget() { + /// extern Target TheFooTarget; + /// RegisterAsmInfo<FooTargetAsmInfo> X(TheFooTarget); + /// } + template<class TargetAsmInfoImpl> + struct RegisterAsmInfo { + RegisterAsmInfo(Target &T) { + TargetRegistry::RegisterAsmInfo(T, &Allocator); + } + private: + static const TargetAsmInfo *Allocator(const Target &T, const StringRef &TT){ + return new TargetAsmInfoImpl(T, TT); + } + + }; + + /// RegisterAsmInfoFn - Helper template for registering a target assembly info + /// implementation. This invokes the specified function to do the + /// construction. Usage: + /// + /// extern "C" void LLVMInitializeFooTarget() { + /// extern Target TheFooTarget; + /// RegisterAsmInfoFn X(TheFooTarget, TheFunction); + /// } + struct RegisterAsmInfoFn { + RegisterAsmInfoFn(Target &T, Target::AsmInfoCtorFnTy Fn) { + TargetRegistry::RegisterAsmInfo(T, Fn); + } + }; + + /// RegisterTargetMachine - Helper template for registering a target machine /// implementation, for use in the target machine initialization /// function. Usage: diff --git a/lib/CodeGen/LLVMTargetMachine.cpp b/lib/CodeGen/LLVMTargetMachine.cpp index 6a456dc0e6..71efa1375e 100644 --- a/lib/CodeGen/LLVMTargetMachine.cpp +++ b/lib/CodeGen/LLVMTargetMachine.cpp @@ -55,6 +55,15 @@ static cl::opt<cl::boolOrDefault> EnableFastISelOption("fast-isel", cl::Hidden, cl::desc("Enable the experimental \"fast\" instruction selector")); + +LLVMTargetMachine::LLVMTargetMachine(const Target &T, + const std::string &TargetTriple) + : TargetMachine(T) { + AsmInfo = T.createAsmInfo(TargetTriple); +} + + + FileModel::Model LLVMTargetMachine::addPassesToEmitFile(PassManagerBase &PM, formatted_raw_ostream &Out, diff --git a/lib/Target/ARM/ARMTargetMachine.cpp b/lib/Target/ARM/ARMTargetMachine.cpp index c66570b2e3..fbc5f38efe 100644 --- a/lib/Target/ARM/ARMTargetMachine.cpp +++ b/lib/Target/ARM/ARMTargetMachine.cpp @@ -27,10 +27,26 @@ static cl::opt<bool> DisableLdStOpti("disable-arm-loadstore-opti", cl::Hidden, static cl::opt<bool> DisableIfConversion("disable-arm-if-conversion",cl::Hidden, cl::desc("Disable if-conversion pass")); +static const TargetAsmInfo *createTargetAsmInfo(const Target &T, + const StringRef &TT) { + Triple TheTriple(TT); + switch (TheTriple.getOS()) { + case Triple::Darwin: + return new ARMDarwinTargetAsmInfo(); + default: + return new ARMELFTargetAsmInfo(); + } +} + + extern "C" void LLVMInitializeARMTarget() { // Register the target. RegisterTargetMachine<ARMTargetMachine> X(TheARMTarget); RegisterTargetMachine<ThumbTargetMachine> Y(TheThumbTarget); + + // Register the target asm info. + RegisterAsmInfoFn A(TheARMTarget, createTargetAsmInfo); + RegisterAsmInfoFn B(TheThumbTarget, createTargetAsmInfo); } /// TargetMachine ctor - Create an ARM architecture model. @@ -73,16 +89,6 @@ ThumbTargetMachine::ThumbTargetMachine(const Target &T, const std::string &TT, } -const TargetAsmInfo *ARMBaseTargetMachine::createTargetAsmInfo() const { - switch (Subtarget.TargetType) { - default: llvm_unreachable("Unknown ARM subtarget kind"); - case ARMSubtarget::isDarwin: - return new ARMDarwinTargetAsmInfo(); - case ARMSubtarget::isELF: - return new ARMELFTargetAsmInfo(); - } -} - // Pass Pipeline Configuration bool ARMBaseTargetMachine::addInstSelector(PassManagerBase &PM, diff --git a/lib/Target/ARM/ARMTargetMachine.h b/lib/Target/ARM/ARMTargetMachine.h index d992f08f2f..420305500f 100644 --- a/lib/Target/ARM/ARMTargetMachine.h +++ b/lib/Target/ARM/ARMTargetMachine.h @@ -47,8 +47,6 @@ public: return InstrItins; } - virtual const TargetAsmInfo *createTargetAsmInfo() const; - // Pass Pipeline Configuration virtual bool addInstSelector(PassManagerBase &PM, CodeGenOpt::Level OptLevel); virtual bool addPreRegAlloc(PassManagerBase &PM, CodeGenOpt::Level OptLevel); diff --git a/lib/Target/Alpha/AlphaTargetAsmInfo.cpp b/lib/Target/Alpha/AlphaTargetAsmInfo.cpp index 20be50eff9..ebb89ecf52 100644 --- a/lib/Target/Alpha/AlphaTargetAsmInfo.cpp +++ b/lib/Target/Alpha/AlphaTargetAsmInfo.cpp @@ -14,7 +14,7 @@ #include "AlphaTargetAsmInfo.h" using namespace llvm; -AlphaTargetAsmInfo::AlphaTargetAsmInfo() { +AlphaTargetAsmInfo::AlphaTargetAsmInfo(const Target &T, const StringRef &TT) { AlignmentIsInBytes = false; PrivateGlobalPrefix = "$"; PICJumpTableDirective = ".gprel32"; diff --git a/lib/Target/Alpha/AlphaTargetAsmInfo.h b/lib/Target/Alpha/AlphaTargetAsmInfo.h index 1f0259b22c..20903c730f 100644 --- a/lib/Target/Alpha/AlphaTargetAsmInfo.h +++ b/lib/Target/Alpha/AlphaTargetAsmInfo.h @@ -17,9 +17,11 @@ #include "llvm/Target/TargetAsmInfo.h" namespace llvm { + class Target; + class StringRef; struct AlphaTargetAsmInfo : public TargetAsmInfo { - explicit AlphaTargetAsmInfo(); + explicit AlphaTargetAsmInfo(const Target &T, const StringRef &TT); }; } // namespace llvm diff --git a/lib/Target/Alpha/AlphaTargetMachine.cpp b/lib/Target/Alpha/AlphaTargetMachine.cpp index bb140dca92..19218704a6 100644 --- a/lib/Target/Alpha/AlphaTargetMachine.cpp +++ b/lib/Target/Alpha/AlphaTargetMachine.cpp @@ -17,16 +17,12 @@ #include "llvm/PassManager.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Target/TargetRegistry.h" - using namespace llvm; extern "C" void LLVMInitializeAlphaTarget() { // Register the target. RegisterTargetMachine<AlphaTargetMachine> X(TheAlphaTarget); -} - -const TargetAsmInfo *AlphaTargetMachine::createTargetAsmInfo() const { - return new AlphaTargetAsmInfo(); + RegisterAsmInfo<AlphaTargetAsmInfo> Y(TheAlphaTarget); } AlphaTargetMachine::AlphaTargetMachine(const Target &T, const std::string &TT, diff --git a/lib/Target/Alpha/AlphaTargetMachine.h b/lib/Target/Alpha/AlphaTargetMachine.h index fc6439345a..f03e9388f7 100644 --- a/lib/Target/Alpha/AlphaTargetMachine.h +++ b/lib/Target/Alpha/AlphaTargetMachine.h @@ -34,9 +34,6 @@ class AlphaTargetMachine : public LLVMTargetMachine { AlphaSubtarget Subtarget; AlphaTargetLowering TLInfo; -protected: - virtual const TargetAsmInfo *createTargetAsmInfo() const; - public: AlphaTargetMachine(const Target &T, const std::string &TT, const std::string &FS); diff --git a/lib/Target/Blackfin/BlackfinTargetAsmInfo.cpp b/lib/Target/Blackfin/BlackfinTargetAsmInfo.cpp index 3ab02d8c51..21087453ef 100644 --- a/lib/Target/Blackfin/BlackfinTargetAsmInfo.cpp +++ b/lib/Target/Blackfin/BlackfinTargetAsmInfo.cpp @@ -15,7 +15,8 @@ using namespace llvm; -BlackfinTargetAsmInfo::BlackfinTargetAsmInfo() { +BlackfinTargetAsmInfo::BlackfinTargetAsmInfo(const Target &T, + const StringRef &TT) { GlobalPrefix = "_"; CommentString = "//"; } diff --git a/lib/Target/Blackfin/BlackfinTargetAsmInfo.h b/lib/Target/Blackfin/BlackfinTargetAsmInfo.h index fa4cd7c373..8040f0fb21 100644 --- a/lib/Target/Blackfin/BlackfinTargetAsmInfo.h +++ b/lib/Target/Blackfin/BlackfinTargetAsmInfo.h @@ -17,9 +17,11 @@ #include "llvm/Target/TargetAsmInfo.h" namespace llvm { + class Target; + class StringRef; struct BlackfinTargetAsmInfo : public TargetAsmInfo { - explicit BlackfinTargetAsmInfo(); + explicit BlackfinTargetAsmInfo(const Target &T, const StringRef &TT); }; } // namespace llvm diff --git a/lib/Target/Blackfin/BlackfinTargetMachine.cpp b/lib/Target/Blackfin/BlackfinTargetMachine.cpp index 35dba2568d..4309a9be4d 100644 --- a/lib/Target/Blackfin/BlackfinTargetMachine.cpp +++ b/lib/Target/Blackfin/BlackfinTargetMachine.cpp @@ -20,10 +20,8 @@ using namespace llvm; extern "C" void LLVMInitializeBlackfinTarget() { RegisterTargetMachine<BlackfinTargetMachine> X(TheBlackfinTarget); -} + RegisterAsmInfo<BlackfinTargetAsmInfo> Y(TheBlackfinTarget); -const TargetAsmInfo* BlackfinTargetMachine::createTargetAsmInfo() const { - return new BlackfinTargetAsmInfo(); } BlackfinTargetMachine::BlackfinTargetMachine(const Target &T, diff --git a/lib/Target/Blackfin/BlackfinTargetMachine.h b/lib/Target/Blackfin/BlackfinTargetMachine.h index 13a8a631f3..73ed3143f5 100644 --- a/lib/Target/Blackfin/BlackfinTargetMachine.h +++ b/lib/Target/Blackfin/BlackfinTargetMachine.h @@ -29,10 +29,6 @@ namespace llvm { BlackfinTargetLowering TLInfo; BlackfinInstrInfo InstrInfo; TargetFrameInfo FrameInfo; - - protected: - virtual const TargetAsmInfo *createTargetAsmInfo() const; - public: BlackfinTargetMachine(const Target &T, const std::string &TT, const std::string &FS); diff --git a/lib/Target/CellSPU/SPUTargetAsmInfo.cpp b/lib/Target/CellSPU/SPUTargetAsmInfo.cpp index a295a4c29d..4ae852df99 100644 --- a/lib/Target/CellSPU/SPUTargetAsmInfo.cpp +++ b/lib/Target/CellSPU/SPUTargetAsmInfo.cpp @@ -14,7 +14,7 @@ #include "SPUTargetAsmInfo.h" using namespace llvm; -SPULinuxTargetAsmInfo::SPULinuxTargetAsmInfo() { +SPULinuxTargetAsmInfo::SPULinuxTargetAsmInfo(const Target &T, const StringRef &TT) { ZeroDirective = "\t.space\t"; SetDirective = "\t.set"; Data64bitsDirective = "\t.quad\t"; diff --git a/lib/Target/CellSPU/SPUTargetAsmInfo.h b/lib/Target/CellSPU/SPUTargetAsmInfo.h index 9507ff9e59..b3c6bdaefd 100644 --- a/lib/Target/CellSPU/SPUTargetAsmInfo.h +++ b/lib/Target/CellSPU/SPUTargetAsmInfo.h @@ -17,9 +17,11 @@ #include "llvm/Target/TargetAsmInfo.h" namespace llvm { - + class Target; + class StringRef; + struct SPULinuxTargetAsmInfo : public TargetAsmInfo { - explicit SPULinuxTargetAsmInfo(); + explicit SPULinuxTargetAsmInfo(const Target &T, const StringRef &TT); }; } // namespace llvm diff --git a/lib/Target/CellSPU/SPUTargetMachine.cpp b/lib/Target/CellSPU/SPUTargetMachine.cpp index 85dda310d2..b904b9559d 100644 --- a/lib/Target/CellSPU/SPUTargetMachine.cpp +++ b/lib/Target/CellSPU/SPUTargetMachine.cpp @@ -25,6 +25,7 @@ using namespace llvm; extern "C" void LLVMInitializeCellSPUTarget() { // Register the target. RegisterTargetMachine<SPUTargetMachine> X(TheCellSPUTarget); + RegisterAsmInfo<SPULinuxTargetAsmInfo> Y(TheCellSPUTarget); } const std::pair<unsigned, int> * @@ -33,10 +34,6 @@ SPUFrameInfo::getCalleeSaveSpillSlots(unsigned &NumEntries) const { return &LR[0]; } -const TargetAsmInfo *SPUTargetMachine::createTargetAsmInfo() const { - return new SPULinuxTargetAsmInfo(); -} - SPUTargetMachine::SPUTargetMachine(const Target &T, const std::string &TT, const std::string &FS) : LLVMTargetMachine(T, TT), diff --git a/lib/Target/CellSPU/SPUTargetMachine.h b/lib/Target/CellSPU/SPUTargetMachine.h index a0e7130749..9fdcfe9ab6 100644 --- a/lib/Target/CellSPU/SPUTargetMachine.h +++ b/lib/Target/CellSPU/SPUTargetMachine.h @@ -35,10 +35,6 @@ class SPUTargetMachine : public LLVMTargetMachine { SPUFrameInfo FrameInfo; SPUTargetLowering TLInfo; InstrItineraryData InstrItins; - -protected: - virtual const TargetAsmInfo *createTargetAsmInfo() const; - public: SPUTargetMachine(const Target &T, const std::string &TT, const std::string &FS); diff --git a/lib/Target/MSP430/MSP430AsmPrinter.cpp b/lib/Target/MSP430/MSP430AsmPrinter.cpp index 6209fc4c0a..fd8c7d1715 100644 --- a/lib/Target/MSP430/MSP430AsmPrinter.cpp +++ b/lib/Target/MSP430/MSP430AsmPrinter.cpp @@ -15,6 +15,7 @@ #define DEBUG_TYPE "asm-printer" #include "MSP430.h" #include "MSP430InstrInfo.h" +#include "MSP430TargetAsmInfo.h" #include "MSP430TargetMachine.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" @@ -25,7 +26,6 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineInstr.h" -#include "llvm/Target/TargetAsmInfo.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetRegistry.h" @@ -246,4 +246,5 @@ extern "C" void LLVMInitializeMSP430Target() { // Register the target. RegisterTargetMachine<MSP430TargetMachine> X(TheMSP430Target); RegisterAsmPrinter<MSP430AsmPrinter> Y(TheMSP430Target); + RegisterAsmInfo<MSP430TargetAsmInfo> Z(TheMSP430Target); } diff --git a/lib/Target/MSP430/MSP430TargetAsmInfo.cpp b/lib/Target/MSP430/MSP430TargetAsmInfo.cpp index 1aae9f259d..56560d6c1e 100644 --- a/lib/Target/MSP430/MSP430TargetAsmInfo.cpp +++ b/lib/Target/MSP430/MSP430TargetAsmInfo.cpp @@ -14,6 +14,6 @@ #include "MSP430TargetAsmInfo.h" using namespace llvm; -MSP430TargetAsmInfo::MSP430TargetAsmInfo() { +MSP430TargetAsmInfo::MSP430TargetAsmInfo(const Target &T, const StringRef &TT) { AlignmentIsInBytes = false; } diff --git a/lib/Target/MSP430/MSP430TargetAsmInfo.h b/lib/Target/MSP430/MSP430TargetAsmInfo.h index 8b4580c531..510e2390f8 100644 --- a/lib/Target/MSP430/MSP430TargetAsmInfo.h +++ b/lib/Target/MSP430/MSP430TargetAsmInfo.h @@ -17,8 +17,10 @@ #include "llvm/Target/TargetAsmInfo.h" namespace llvm { + class Target; + class StringRef; struct MSP430TargetAsmInfo : public TargetAsmInfo { - explicit MSP430TargetAsmInfo(); + explicit MSP430TargetAsmInfo(const Target &T, const StringRef &TT); }; } // namespace llvm diff --git a/lib/Target/MSP430/MSP430TargetMachine.cpp b/lib/Target/MSP430/MSP430TargetMachine.cpp index f34e3db32b..56495e6b21 100644 --- a/lib/Target/MSP430/MSP430TargetMachine.cpp +++ b/lib/Target/MSP430/MSP430TargetMachine.cpp @@ -29,9 +29,6 @@ MSP430TargetMachine::MSP430TargetMachine(const Target &T, InstrInfo(*this), TLInfo(*this), FrameInfo(TargetFrameInfo::StackGrowsDown, 2, -2) { } -const TargetAsmInfo *MSP430TargetMachine::createTargetAsmInfo() const { - return new MSP430TargetAsmInfo(); -} bool MSP430TargetMachine::addInstSelector(PassManagerBase &PM, CodeGenOpt::Level OptLevel) { diff --git a/lib/Target/MSP430/MSP430TargetMachine.h b/lib/Target/MSP430/MSP430TargetMachine.h index 44cdd54239..d38614018c 100644 --- a/lib/Target/MSP430/MSP430TargetMachine.h +++ b/lib/Target/MSP430/MSP430TargetMachine.h @@ -37,9 +37,6 @@ class MSP430TargetMachine : public LLVMTargetMachine { // any MSP430 specific FrameInfo class. TargetFrameInfo FrameInfo; -protected: - virtual const TargetAsmInfo *createTargetAsmInfo() const; - public: MSP430TargetMachine(const Target &T, const std::string &TT, const std::string &FS); diff --git a/lib/Target/Mips/MipsTargetAsmInfo.cpp b/lib/Target/Mips/MipsTargetAsmInfo.cpp index f587172645..3046f33ac0 100644 --- a/lib/Target/Mips/MipsTargetAsmInfo.cpp +++ b/lib/Target/Mips/MipsTargetAsmInfo.cpp @@ -14,7 |