1*0b57cec5SDimitry Andric //===-- llvm/CodeGen/MachineModuleInfo.cpp ----------------------*- C++ -*-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 10*0b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 11*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 12*0b57cec5SDimitry Andric #include "llvm/ADT/PostOrderIterator.h" 13*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 14*0b57cec5SDimitry Andric #include "llvm/ADT/TinyPtrVector.h" 15*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 16*0b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 17*0b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h" 18*0b57cec5SDimitry Andric #include "llvm/IR/DerivedTypes.h" 19*0b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 20*0b57cec5SDimitry Andric #include "llvm/IR/Module.h" 21*0b57cec5SDimitry Andric #include "llvm/IR/Value.h" 22*0b57cec5SDimitry Andric #include "llvm/IR/ValueHandle.h" 23*0b57cec5SDimitry Andric #include "llvm/MC/MCContext.h" 24*0b57cec5SDimitry Andric #include "llvm/MC/MCSymbol.h" 25*0b57cec5SDimitry Andric #include "llvm/Pass.h" 26*0b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 27*0b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 28*0b57cec5SDimitry Andric #include "llvm/Target/TargetLoweringObjectFile.h" 29*0b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h" 30*0b57cec5SDimitry Andric #include <algorithm> 31*0b57cec5SDimitry Andric #include <cassert> 32*0b57cec5SDimitry Andric #include <memory> 33*0b57cec5SDimitry Andric #include <utility> 34*0b57cec5SDimitry Andric #include <vector> 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric using namespace llvm; 37*0b57cec5SDimitry Andric using namespace llvm::dwarf; 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric // Handle the Pass registration stuff necessary to use DataLayout's. 40*0b57cec5SDimitry Andric INITIALIZE_PASS(MachineModuleInfo, "machinemoduleinfo", 41*0b57cec5SDimitry Andric "Machine Module Information", false, false) 42*0b57cec5SDimitry Andric char MachineModuleInfo::ID = 0; 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric // Out of line virtual method. 45*0b57cec5SDimitry Andric MachineModuleInfoImpl::~MachineModuleInfoImpl() = default; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric namespace llvm { 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric class MMIAddrLabelMapCallbackPtr final : CallbackVH { 50*0b57cec5SDimitry Andric MMIAddrLabelMap *Map = nullptr; 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric public: 53*0b57cec5SDimitry Andric MMIAddrLabelMapCallbackPtr() = default; 54*0b57cec5SDimitry Andric MMIAddrLabelMapCallbackPtr(Value *V) : CallbackVH(V) {} 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric void setPtr(BasicBlock *BB) { 57*0b57cec5SDimitry Andric ValueHandleBase::operator=(BB); 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric void setMap(MMIAddrLabelMap *map) { Map = map; } 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric void deleted() override; 63*0b57cec5SDimitry Andric void allUsesReplacedWith(Value *V2) override; 64*0b57cec5SDimitry Andric }; 65*0b57cec5SDimitry Andric 66*0b57cec5SDimitry Andric class MMIAddrLabelMap { 67*0b57cec5SDimitry Andric MCContext &Context; 68*0b57cec5SDimitry Andric struct AddrLabelSymEntry { 69*0b57cec5SDimitry Andric /// The symbols for the label. 70*0b57cec5SDimitry Andric TinyPtrVector<MCSymbol *> Symbols; 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric Function *Fn; // The containing function of the BasicBlock. 73*0b57cec5SDimitry Andric unsigned Index; // The index in BBCallbacks for the BasicBlock. 74*0b57cec5SDimitry Andric }; 75*0b57cec5SDimitry Andric 76*0b57cec5SDimitry Andric DenseMap<AssertingVH<BasicBlock>, AddrLabelSymEntry> AddrLabelSymbols; 77*0b57cec5SDimitry Andric 78*0b57cec5SDimitry Andric /// Callbacks for the BasicBlock's that we have entries for. We use this so 79*0b57cec5SDimitry Andric /// we get notified if a block is deleted or RAUWd. 80*0b57cec5SDimitry Andric std::vector<MMIAddrLabelMapCallbackPtr> BBCallbacks; 81*0b57cec5SDimitry Andric 82*0b57cec5SDimitry Andric /// This is a per-function list of symbols whose corresponding BasicBlock got 83*0b57cec5SDimitry Andric /// deleted. These symbols need to be emitted at some point in the file, so 84*0b57cec5SDimitry Andric /// AsmPrinter emits them after the function body. 85*0b57cec5SDimitry Andric DenseMap<AssertingVH<Function>, std::vector<MCSymbol*>> 86*0b57cec5SDimitry Andric DeletedAddrLabelsNeedingEmission; 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric public: 89*0b57cec5SDimitry Andric MMIAddrLabelMap(MCContext &context) : Context(context) {} 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric ~MMIAddrLabelMap() { 92*0b57cec5SDimitry Andric assert(DeletedAddrLabelsNeedingEmission.empty() && 93*0b57cec5SDimitry Andric "Some labels for deleted blocks never got emitted"); 94*0b57cec5SDimitry Andric } 95*0b57cec5SDimitry Andric 96*0b57cec5SDimitry Andric ArrayRef<MCSymbol *> getAddrLabelSymbolToEmit(BasicBlock *BB); 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric void takeDeletedSymbolsForFunction(Function *F, 99*0b57cec5SDimitry Andric std::vector<MCSymbol*> &Result); 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric void UpdateForDeletedBlock(BasicBlock *BB); 102*0b57cec5SDimitry Andric void UpdateForRAUWBlock(BasicBlock *Old, BasicBlock *New); 103*0b57cec5SDimitry Andric }; 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric } // end namespace llvm 106*0b57cec5SDimitry Andric 107*0b57cec5SDimitry Andric ArrayRef<MCSymbol *> MMIAddrLabelMap::getAddrLabelSymbolToEmit(BasicBlock *BB) { 108*0b57cec5SDimitry Andric assert(BB->hasAddressTaken() && 109*0b57cec5SDimitry Andric "Shouldn't get label for block without address taken"); 110*0b57cec5SDimitry Andric AddrLabelSymEntry &Entry = AddrLabelSymbols[BB]; 111*0b57cec5SDimitry Andric 112*0b57cec5SDimitry Andric // If we already had an entry for this block, just return it. 113*0b57cec5SDimitry Andric if (!Entry.Symbols.empty()) { 114*0b57cec5SDimitry Andric assert(BB->getParent() == Entry.Fn && "Parent changed"); 115*0b57cec5SDimitry Andric return Entry.Symbols; 116*0b57cec5SDimitry Andric } 117*0b57cec5SDimitry Andric 118*0b57cec5SDimitry Andric // Otherwise, this is a new entry, create a new symbol for it and add an 119*0b57cec5SDimitry Andric // entry to BBCallbacks so we can be notified if the BB is deleted or RAUWd. 120*0b57cec5SDimitry Andric BBCallbacks.emplace_back(BB); 121*0b57cec5SDimitry Andric BBCallbacks.back().setMap(this); 122*0b57cec5SDimitry Andric Entry.Index = BBCallbacks.size() - 1; 123*0b57cec5SDimitry Andric Entry.Fn = BB->getParent(); 124*0b57cec5SDimitry Andric Entry.Symbols.push_back(Context.createTempSymbol(!BB->hasAddressTaken())); 125*0b57cec5SDimitry Andric return Entry.Symbols; 126*0b57cec5SDimitry Andric } 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric /// If we have any deleted symbols for F, return them. 129*0b57cec5SDimitry Andric void MMIAddrLabelMap:: 130*0b57cec5SDimitry Andric takeDeletedSymbolsForFunction(Function *F, std::vector<MCSymbol*> &Result) { 131*0b57cec5SDimitry Andric DenseMap<AssertingVH<Function>, std::vector<MCSymbol*>>::iterator I = 132*0b57cec5SDimitry Andric DeletedAddrLabelsNeedingEmission.find(F); 133*0b57cec5SDimitry Andric 134*0b57cec5SDimitry Andric // If there are no entries for the function, just return. 135*0b57cec5SDimitry Andric if (I == DeletedAddrLabelsNeedingEmission.end()) return; 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric // Otherwise, take the list. 138*0b57cec5SDimitry Andric std::swap(Result, I->second); 139*0b57cec5SDimitry Andric DeletedAddrLabelsNeedingEmission.erase(I); 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric 142*0b57cec5SDimitry Andric void MMIAddrLabelMap::UpdateForDeletedBlock(BasicBlock *BB) { 143*0b57cec5SDimitry Andric // If the block got deleted, there is no need for the symbol. If the symbol 144*0b57cec5SDimitry Andric // was already emitted, we can just forget about it, otherwise we need to 145*0b57cec5SDimitry Andric // queue it up for later emission when the function is output. 146*0b57cec5SDimitry Andric AddrLabelSymEntry Entry = std::move(AddrLabelSymbols[BB]); 147*0b57cec5SDimitry Andric AddrLabelSymbols.erase(BB); 148*0b57cec5SDimitry Andric assert(!Entry.Symbols.empty() && "Didn't have a symbol, why a callback?"); 149*0b57cec5SDimitry Andric BBCallbacks[Entry.Index] = nullptr; // Clear the callback. 150*0b57cec5SDimitry Andric 151*0b57cec5SDimitry Andric assert((BB->getParent() == nullptr || BB->getParent() == Entry.Fn) && 152*0b57cec5SDimitry Andric "Block/parent mismatch"); 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric for (MCSymbol *Sym : Entry.Symbols) { 155*0b57cec5SDimitry Andric if (Sym->isDefined()) 156*0b57cec5SDimitry Andric return; 157*0b57cec5SDimitry Andric 158*0b57cec5SDimitry Andric // If the block is not yet defined, we need to emit it at the end of the 159*0b57cec5SDimitry Andric // function. Add the symbol to the DeletedAddrLabelsNeedingEmission list 160*0b57cec5SDimitry Andric // for the containing Function. Since the block is being deleted, its 161*0b57cec5SDimitry Andric // parent may already be removed, we have to get the function from 'Entry'. 162*0b57cec5SDimitry Andric DeletedAddrLabelsNeedingEmission[Entry.Fn].push_back(Sym); 163*0b57cec5SDimitry Andric } 164*0b57cec5SDimitry Andric } 165*0b57cec5SDimitry Andric 166*0b57cec5SDimitry Andric void MMIAddrLabelMap::UpdateForRAUWBlock(BasicBlock *Old, BasicBlock *New) { 167*0b57cec5SDimitry Andric // Get the entry for the RAUW'd block and remove it from our map. 168*0b57cec5SDimitry Andric AddrLabelSymEntry OldEntry = std::move(AddrLabelSymbols[Old]); 169*0b57cec5SDimitry Andric AddrLabelSymbols.erase(Old); 170*0b57cec5SDimitry Andric assert(!OldEntry.Symbols.empty() && "Didn't have a symbol, why a callback?"); 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric AddrLabelSymEntry &NewEntry = AddrLabelSymbols[New]; 173*0b57cec5SDimitry Andric 174*0b57cec5SDimitry Andric // If New is not address taken, just move our symbol over to it. 175*0b57cec5SDimitry Andric if (NewEntry.Symbols.empty()) { 176*0b57cec5SDimitry Andric BBCallbacks[OldEntry.Index].setPtr(New); // Update the callback. 177*0b57cec5SDimitry Andric NewEntry = std::move(OldEntry); // Set New's entry. 178*0b57cec5SDimitry Andric return; 179*0b57cec5SDimitry Andric } 180*0b57cec5SDimitry Andric 181*0b57cec5SDimitry Andric BBCallbacks[OldEntry.Index] = nullptr; // Update the callback. 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric // Otherwise, we need to add the old symbols to the new block's set. 184*0b57cec5SDimitry Andric NewEntry.Symbols.insert(NewEntry.Symbols.end(), OldEntry.Symbols.begin(), 185*0b57cec5SDimitry Andric OldEntry.Symbols.end()); 186*0b57cec5SDimitry Andric } 187*0b57cec5SDimitry Andric 188*0b57cec5SDimitry Andric void MMIAddrLabelMapCallbackPtr::deleted() { 189*0b57cec5SDimitry Andric Map->UpdateForDeletedBlock(cast<BasicBlock>(getValPtr())); 190*0b57cec5SDimitry Andric } 191*0b57cec5SDimitry Andric 192*0b57cec5SDimitry Andric void MMIAddrLabelMapCallbackPtr::allUsesReplacedWith(Value *V2) { 193*0b57cec5SDimitry Andric Map->UpdateForRAUWBlock(cast<BasicBlock>(getValPtr()), cast<BasicBlock>(V2)); 194*0b57cec5SDimitry Andric } 195*0b57cec5SDimitry Andric 196*0b57cec5SDimitry Andric MachineModuleInfo::MachineModuleInfo(const LLVMTargetMachine *TM) 197*0b57cec5SDimitry Andric : ImmutablePass(ID), TM(*TM), 198*0b57cec5SDimitry Andric Context(TM->getMCAsmInfo(), TM->getMCRegisterInfo(), 199*0b57cec5SDimitry Andric TM->getObjFileLowering(), nullptr, false) { 200*0b57cec5SDimitry Andric initializeMachineModuleInfoPass(*PassRegistry::getPassRegistry()); 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric MachineModuleInfo::~MachineModuleInfo() = default; 204*0b57cec5SDimitry Andric 205*0b57cec5SDimitry Andric bool MachineModuleInfo::doInitialization(Module &M) { 206*0b57cec5SDimitry Andric ObjFileMMI = nullptr; 207*0b57cec5SDimitry Andric CurCallSite = 0; 208*0b57cec5SDimitry Andric UsesMSVCFloatingPoint = UsesMorestackAddr = false; 209*0b57cec5SDimitry Andric HasSplitStack = HasNosplitStack = false; 210*0b57cec5SDimitry Andric AddrLabelSymbols = nullptr; 211*0b57cec5SDimitry Andric TheModule = &M; 212*0b57cec5SDimitry Andric DbgInfoAvailable = !llvm::empty(M.debug_compile_units()); 213*0b57cec5SDimitry Andric return false; 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric 216*0b57cec5SDimitry Andric bool MachineModuleInfo::doFinalization(Module &M) { 217*0b57cec5SDimitry Andric Personalities.clear(); 218*0b57cec5SDimitry Andric 219*0b57cec5SDimitry Andric delete AddrLabelSymbols; 220*0b57cec5SDimitry Andric AddrLabelSymbols = nullptr; 221*0b57cec5SDimitry Andric 222*0b57cec5SDimitry Andric Context.reset(); 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric delete ObjFileMMI; 225*0b57cec5SDimitry Andric ObjFileMMI = nullptr; 226*0b57cec5SDimitry Andric 227*0b57cec5SDimitry Andric return false; 228*0b57cec5SDimitry Andric } 229*0b57cec5SDimitry Andric 230*0b57cec5SDimitry Andric //===- Address of Block Management ----------------------------------------===// 231*0b57cec5SDimitry Andric 232*0b57cec5SDimitry Andric ArrayRef<MCSymbol *> 233*0b57cec5SDimitry Andric MachineModuleInfo::getAddrLabelSymbolToEmit(const BasicBlock *BB) { 234*0b57cec5SDimitry Andric // Lazily create AddrLabelSymbols. 235*0b57cec5SDimitry Andric if (!AddrLabelSymbols) 236*0b57cec5SDimitry Andric AddrLabelSymbols = new MMIAddrLabelMap(Context); 237*0b57cec5SDimitry Andric return AddrLabelSymbols->getAddrLabelSymbolToEmit(const_cast<BasicBlock*>(BB)); 238*0b57cec5SDimitry Andric } 239*0b57cec5SDimitry Andric 240*0b57cec5SDimitry Andric void MachineModuleInfo:: 241*0b57cec5SDimitry Andric takeDeletedSymbolsForFunction(const Function *F, 242*0b57cec5SDimitry Andric std::vector<MCSymbol*> &Result) { 243*0b57cec5SDimitry Andric // If no blocks have had their addresses taken, we're done. 244*0b57cec5SDimitry Andric if (!AddrLabelSymbols) return; 245*0b57cec5SDimitry Andric return AddrLabelSymbols-> 246*0b57cec5SDimitry Andric takeDeletedSymbolsForFunction(const_cast<Function*>(F), Result); 247*0b57cec5SDimitry Andric } 248*0b57cec5SDimitry Andric 249*0b57cec5SDimitry Andric /// \name Exception Handling 250*0b57cec5SDimitry Andric /// \{ 251*0b57cec5SDimitry Andric 252*0b57cec5SDimitry Andric void MachineModuleInfo::addPersonality(const Function *Personality) { 253*0b57cec5SDimitry Andric for (unsigned i = 0; i < Personalities.size(); ++i) 254*0b57cec5SDimitry Andric if (Personalities[i] == Personality) 255*0b57cec5SDimitry Andric return; 256*0b57cec5SDimitry Andric Personalities.push_back(Personality); 257*0b57cec5SDimitry Andric } 258*0b57cec5SDimitry Andric 259*0b57cec5SDimitry Andric /// \} 260*0b57cec5SDimitry Andric 261*0b57cec5SDimitry Andric MachineFunction * 262*0b57cec5SDimitry Andric MachineModuleInfo::getMachineFunction(const Function &F) const { 263*0b57cec5SDimitry Andric auto I = MachineFunctions.find(&F); 264*0b57cec5SDimitry Andric return I != MachineFunctions.end() ? I->second.get() : nullptr; 265*0b57cec5SDimitry Andric } 266*0b57cec5SDimitry Andric 267*0b57cec5SDimitry Andric MachineFunction & 268*0b57cec5SDimitry Andric MachineModuleInfo::getOrCreateMachineFunction(const Function &F) { 269*0b57cec5SDimitry Andric // Shortcut for the common case where a sequence of MachineFunctionPasses 270*0b57cec5SDimitry Andric // all query for the same Function. 271*0b57cec5SDimitry Andric if (LastRequest == &F) 272*0b57cec5SDimitry Andric return *LastResult; 273*0b57cec5SDimitry Andric 274*0b57cec5SDimitry Andric auto I = MachineFunctions.insert( 275*0b57cec5SDimitry Andric std::make_pair(&F, std::unique_ptr<MachineFunction>())); 276*0b57cec5SDimitry Andric MachineFunction *MF; 277*0b57cec5SDimitry Andric if (I.second) { 278*0b57cec5SDimitry Andric // No pre-existing machine function, create a new one. 279*0b57cec5SDimitry Andric const TargetSubtargetInfo &STI = *TM.getSubtargetImpl(F); 280*0b57cec5SDimitry Andric MF = new MachineFunction(F, TM, STI, NextFnNum++, *this); 281*0b57cec5SDimitry Andric // Update the set entry. 282*0b57cec5SDimitry Andric I.first->second.reset(MF); 283*0b57cec5SDimitry Andric } else { 284*0b57cec5SDimitry Andric MF = I.first->second.get(); 285*0b57cec5SDimitry Andric } 286*0b57cec5SDimitry Andric 287*0b57cec5SDimitry Andric LastRequest = &F; 288*0b57cec5SDimitry Andric LastResult = MF; 289*0b57cec5SDimitry Andric return *MF; 290*0b57cec5SDimitry Andric } 291*0b57cec5SDimitry Andric 292*0b57cec5SDimitry Andric void MachineModuleInfo::deleteMachineFunctionFor(Function &F) { 293*0b57cec5SDimitry Andric MachineFunctions.erase(&F); 294*0b57cec5SDimitry Andric LastRequest = nullptr; 295*0b57cec5SDimitry Andric LastResult = nullptr; 296*0b57cec5SDimitry Andric } 297*0b57cec5SDimitry Andric 298*0b57cec5SDimitry Andric namespace { 299*0b57cec5SDimitry Andric 300*0b57cec5SDimitry Andric /// This pass frees the MachineFunction object associated with a Function. 301*0b57cec5SDimitry Andric class FreeMachineFunction : public FunctionPass { 302*0b57cec5SDimitry Andric public: 303*0b57cec5SDimitry Andric static char ID; 304*0b57cec5SDimitry Andric 305*0b57cec5SDimitry Andric FreeMachineFunction() : FunctionPass(ID) {} 306*0b57cec5SDimitry Andric 307*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 308*0b57cec5SDimitry Andric AU.addRequired<MachineModuleInfo>(); 309*0b57cec5SDimitry Andric AU.addPreserved<MachineModuleInfo>(); 310*0b57cec5SDimitry Andric } 311*0b57cec5SDimitry Andric 312*0b57cec5SDimitry Andric bool runOnFunction(Function &F) override { 313*0b57cec5SDimitry Andric MachineModuleInfo &MMI = getAnalysis<MachineModuleInfo>(); 314*0b57cec5SDimitry Andric MMI.deleteMachineFunctionFor(F); 315*0b57cec5SDimitry Andric return true; 316*0b57cec5SDimitry Andric } 317*0b57cec5SDimitry Andric 318*0b57cec5SDimitry Andric StringRef getPassName() const override { 319*0b57cec5SDimitry Andric return "Free MachineFunction"; 320*0b57cec5SDimitry Andric } 321*0b57cec5SDimitry Andric }; 322*0b57cec5SDimitry Andric 323*0b57cec5SDimitry Andric } // end anonymous namespace 324*0b57cec5SDimitry Andric 325*0b57cec5SDimitry Andric char FreeMachineFunction::ID; 326*0b57cec5SDimitry Andric 327*0b57cec5SDimitry Andric FunctionPass *llvm::createFreeMachineFunctionPass() { 328*0b57cec5SDimitry Andric return new FreeMachineFunction(); 329*0b57cec5SDimitry Andric } 330