SparcAsmPrinter.cpp revision 3bf960ccacf7e226bc9ae66cb77b4098d058e71f
1//===-- SparcV8AsmPrinter.cpp - SparcV8 LLVM assembly writer --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file was developed by the LLVM research group and is distributed under 6// the University of Illinois Open Source License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file contains a printer that converts from our internal representation 11// of machine-dependent LLVM code to GAS-format Sparc V8 assembly language. 12// 13//===----------------------------------------------------------------------===// 14 15#include "SparcV8.h" 16#include "SparcV8InstrInfo.h" 17#include "llvm/Constants.h" 18#include "llvm/DerivedTypes.h" 19#include "llvm/Module.h" 20#include "llvm/Assembly/Writer.h" 21#include "llvm/CodeGen/MachineFunctionPass.h" 22#include "llvm/CodeGen/MachineConstantPool.h" 23#include "llvm/CodeGen/MachineInstr.h" 24#include "llvm/Target/TargetMachine.h" 25#include "llvm/Support/Mangler.h" 26#include "llvm/ADT/Statistic.h" 27#include "llvm/ADT/StringExtras.h" 28#include "llvm/Support/CommandLine.h" 29#include <cctype> 30using namespace llvm; 31 32namespace { 33 Statistic<> EmittedInsts("asm-printer", "Number of machine instrs printed"); 34 35 struct V8Printer : public MachineFunctionPass { 36 /// Output stream on which we're printing assembly code. 37 /// 38 std::ostream &O; 39 40 /// Target machine description which we query for reg. names, data 41 /// layout, etc. 42 /// 43 TargetMachine &TM; 44 45 /// Name-mangler for global names. 46 /// 47 Mangler *Mang; 48 49 V8Printer(std::ostream &o, TargetMachine &tm) : O(o), TM(tm) { } 50 51 /// We name each basic block in a Function with a unique number, so 52 /// that we can consistently refer to them later. This is cleared 53 /// at the beginning of each call to runOnMachineFunction(). 54 /// 55 typedef std::map<const Value *, unsigned> ValueMapTy; 56 ValueMapTy NumberForBB; 57 58 /// Cache of mangled name for current function. This is 59 /// recalculated at the beginning of each call to 60 /// runOnMachineFunction(). 61 /// 62 std::string CurrentFnName; 63 64 virtual const char *getPassName() const { 65 return "SparcV8 Assembly Printer"; 66 } 67 68 void emitConstantValueOnly(const Constant *CV); 69 void emitGlobalConstant(const Constant *CV); 70 void printConstantPool(MachineConstantPool *MCP); 71 void printOperand(const MachineInstr *MI, int opNum); 72 void printBaseOffsetPair (const MachineInstr *MI, int i, bool brackets=true); 73 void printMachineInstruction(const MachineInstr *MI); 74 bool runOnMachineFunction(MachineFunction &F); 75 bool doInitialization(Module &M); 76 bool doFinalization(Module &M); 77 }; 78} // end of anonymous namespace 79 80/// createSparcV8CodePrinterPass - Returns a pass that prints the SparcV8 81/// assembly code for a MachineFunction to the given output stream, 82/// using the given target machine description. This should work 83/// regardless of whether the function is in SSA form. 84/// 85FunctionPass *llvm::createSparcV8CodePrinterPass (std::ostream &o, 86 TargetMachine &tm) { 87 return new V8Printer(o, tm); 88} 89 90/// toOctal - Convert the low order bits of X into an octal digit. 91/// 92static inline char toOctal(int X) { 93 return (X&7)+'0'; 94} 95 96/// getAsCString - Return the specified array as a C compatible 97/// string, only if the predicate isStringCompatible is true. 98/// 99static void printAsCString(std::ostream &O, const ConstantArray *CVA) { 100 assert(CVA->isString() && "Array is not string compatible!"); 101 102 O << "\""; 103 for (unsigned i = 0; i != CVA->getNumOperands(); ++i) { 104 unsigned char C = cast<ConstantInt>(CVA->getOperand(i))->getRawValue(); 105 106 if (C == '"') { 107 O << "\\\""; 108 } else if (C == '\\') { 109 O << "\\\\"; 110 } else if (isprint(C)) { 111 O << C; 112 } else { 113 switch(C) { 114 case '\b': O << "\\b"; break; 115 case '\f': O << "\\f"; break; 116 case '\n': O << "\\n"; break; 117 case '\r': O << "\\r"; break; 118 case '\t': O << "\\t"; break; 119 default: 120 O << '\\'; 121 O << toOctal(C >> 6); 122 O << toOctal(C >> 3); 123 O << toOctal(C >> 0); 124 break; 125 } 126 } 127 } 128 O << "\""; 129} 130 131// Print out the specified constant, without a storage class. Only the 132// constants valid in constant expressions can occur here. 133void V8Printer::emitConstantValueOnly(const Constant *CV) { 134 if (CV->isNullValue() || isa<UndefValue> (CV)) 135 O << "0"; 136 else if (const ConstantBool *CB = dyn_cast<ConstantBool>(CV)) { 137 assert(CB == ConstantBool::True); 138 O << "1"; 139 } else if (const ConstantSInt *CI = dyn_cast<ConstantSInt>(CV)) 140 if (((CI->getValue() << 32) >> 32) == CI->getValue()) 141 O << CI->getValue(); 142 else 143 O << (unsigned long long)CI->getValue(); 144 else if (const ConstantUInt *CI = dyn_cast<ConstantUInt>(CV)) 145 O << CI->getValue(); 146 else if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) 147 // This is a constant address for a global variable or function. Use the 148 // name of the variable or function as the address value. 149 O << Mang->getValueName(GV); 150 else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { 151 const TargetData &TD = TM.getTargetData(); 152 switch(CE->getOpcode()) { 153 case Instruction::GetElementPtr: { 154 // generate a symbolic expression for the byte address 155 const Constant *ptrVal = CE->getOperand(0); 156 std::vector<Value*> idxVec(CE->op_begin()+1, CE->op_end()); 157 if (unsigned Offset = TD.getIndexedOffset(ptrVal->getType(), idxVec)) { 158 O << "("; 159 emitConstantValueOnly(ptrVal); 160 O << ") + " << Offset; 161 } else { 162 emitConstantValueOnly(ptrVal); 163 } 164 break; 165 } 166 case Instruction::Cast: { 167 // Support only non-converting or widening casts for now, that is, ones 168 // that do not involve a change in value. This assertion is really gross, 169 // and may not even be a complete check. 170 Constant *Op = CE->getOperand(0); 171 const Type *OpTy = Op->getType(), *Ty = CE->getType(); 172 173 // Pointers on ILP32 machines can be losslessly converted back and 174 // forth into 32-bit or wider integers, regardless of signedness. 175 assert(((isa<PointerType>(OpTy) 176 && (Ty == Type::LongTy || Ty == Type::ULongTy 177 || Ty == Type::IntTy || Ty == Type::UIntTy)) 178 || (isa<PointerType>(Ty) 179 && (OpTy == Type::LongTy || OpTy == Type::ULongTy 180 || OpTy == Type::IntTy || OpTy == Type::UIntTy)) 181 || (((TD.getTypeSize(Ty) >= TD.getTypeSize(OpTy)) 182 && OpTy->isLosslesslyConvertibleTo(Ty)))) 183 && "FIXME: Don't yet support this kind of constant cast expr"); 184 O << "("; 185 emitConstantValueOnly(Op); 186 O << ")"; 187 break; 188 } 189 case Instruction::Add: 190 O << "("; 191 emitConstantValueOnly(CE->getOperand(0)); 192 O << ") + ("; 193 emitConstantValueOnly(CE->getOperand(1)); 194 O << ")"; 195 break; 196 default: 197 assert(0 && "Unsupported operator!"); 198 } 199 } else { 200 assert(0 && "Unknown constant value!"); 201 } 202} 203 204// Print a constant value or values, with the appropriate storage class as a 205// prefix. 206void V8Printer::emitGlobalConstant(const Constant *CV) { 207 const TargetData &TD = TM.getTargetData(); 208 209 if (const ConstantArray *CVA = dyn_cast<ConstantArray>(CV)) { 210 if (CVA->isString()) { 211 O << "\t.ascii\t"; 212 printAsCString(O, CVA); 213 O << "\n"; 214 } else { // Not a string. Print the values in successive locations 215 for (unsigned i = 0, e = CVA->getNumOperands(); i != e; i++) 216 emitGlobalConstant(CVA->getOperand(i)); 217 } 218 return; 219 } else if (const ConstantStruct *CVS = dyn_cast<ConstantStruct>(CV)) { 220 // Print the fields in successive locations. Pad to align if needed! 221 const StructLayout *cvsLayout = TD.getStructLayout(CVS->getType()); 222 unsigned sizeSoFar = 0; 223 for (unsigned i = 0, e = CVS->getNumOperands(); i != e; i++) { 224 const Constant* field = CVS->getOperand(i); 225 226 // Check if padding is needed and insert one or more 0s. 227 unsigned fieldSize = TD.getTypeSize(field->getType()); 228 unsigned padSize = ((i == e-1? cvsLayout->StructSize 229 : cvsLayout->MemberOffsets[i+1]) 230 - cvsLayout->MemberOffsets[i]) - fieldSize; 231 sizeSoFar += fieldSize + padSize; 232 233 // Now print the actual field value 234 emitGlobalConstant(field); 235 236 // Insert the field padding unless it's zero bytes... 237 if (padSize) 238 O << "\t.skip\t " << padSize << "\n"; 239 } 240 assert(sizeSoFar == cvsLayout->StructSize && 241 "Layout of constant struct may be incorrect!"); 242 return; 243 } else if (const ConstantFP *CFP = dyn_cast<ConstantFP>(CV)) { 244 // FP Constants are printed as integer constants to avoid losing 245 // precision... 246 double Val = CFP->getValue(); 247 switch (CFP->getType()->getTypeID()) { 248 default: assert(0 && "Unknown floating point type!"); 249 case Type::FloatTyID: { 250 union FU { // Abide by C TBAA rules 251 float FVal; 252 unsigned UVal; 253 } U; 254 U.FVal = Val; 255 O << ".long\t" << U.UVal << "\t! float " << Val << "\n"; 256 return; 257 } 258 case Type::DoubleTyID: { 259 union DU { // Abide by C TBAA rules 260 double FVal; 261 uint64_t UVal; 262 } U; 263 U.FVal = Val; 264 O << ".word\t0x" << std::hex << (U.UVal >> 32) << std::dec << "\t! double " << Val << "\n"; 265 O << ".word\t0x" << std::hex << (U.UVal & 0xffffffffUL) << std::dec << "\t! double " << Val << "\n"; 266 return; 267 } 268 } 269 } else if (isa<UndefValue> (CV)) { 270 unsigned size = TD.getTypeSize (CV->getType ()); 271 O << "\t.skip\t " << size << "\n"; 272 return; 273 } else if (isa<ConstantAggregateZero> (CV)) { 274 unsigned size = TD.getTypeSize (CV->getType ()); 275 for (unsigned i = 0; i < size; ++i) 276 O << "\t.byte 0\n"; 277 return; 278 } 279 280 const Type *type = CV->getType(); 281 O << "\t"; 282 switch (type->getTypeID()) { 283 case Type::BoolTyID: case Type::UByteTyID: case Type::SByteTyID: 284 O << ".byte"; 285 break; 286 case Type::UShortTyID: case Type::ShortTyID: 287 O << ".half"; 288 break; 289 case Type::FloatTyID: case Type::PointerTyID: 290 case Type::UIntTyID: case Type::IntTyID: 291 O << ".word"; 292 break; 293 case Type::DoubleTyID: 294 case Type::ULongTyID: case Type::LongTyID: 295 O << ".xword"; 296 break; 297 default: 298 assert (0 && "Can't handle printing this type of thing"); 299 break; 300 } 301 O << "\t"; 302 emitConstantValueOnly(CV); 303 O << "\n"; 304} 305 306/// printConstantPool - Print to the current output stream assembly 307/// representations of the constants in the constant pool MCP. This is 308/// used to print out constants which have been "spilled to memory" by 309/// the code generator. 310/// 311void V8Printer::printConstantPool(MachineConstantPool *MCP) { 312 const std::vector<Constant*> &CP = MCP->getConstants(); 313 const TargetData &TD = TM.getTargetData(); 314 315 if (CP.empty()) return; 316 317 for (unsigned i = 0, e = CP.size(); i != e; ++i) { 318 O << "\t.section \".rodata\"\n"; 319 O << "\t.align " << (unsigned)TD.getTypeAlignment(CP[i]->getType()) 320 << "\n"; 321 O << ".CPI" << CurrentFnName << "_" << i << ":\t\t\t\t\t!" 322 << *CP[i] << "\n"; 323 emitGlobalConstant(CP[i]); 324 } 325} 326 327/// runOnMachineFunction - This uses the printMachineInstruction() 328/// method to print assembly for each instruction. 329/// 330bool V8Printer::runOnMachineFunction(MachineFunction &MF) { 331 // BBNumber is used here so that a given Printer will never give two 332 // BBs the same name. (If you have a better way, please let me know!) 333 static unsigned BBNumber = 0; 334 335 O << "\n\n"; 336 // What's my mangled name? 337 CurrentFnName = Mang->getValueName(MF.getFunction()); 338 339 // Print out constants referenced by the function 340 printConstantPool(MF.getConstantPool()); 341 342 // Print out labels for the function. 343 O << "\t.text\n"; 344 O << "\t.align 16\n"; 345 O << "\t.globl\t" << CurrentFnName << "\n"; 346 O << "\t.type\t" << CurrentFnName << ", #function\n"; 347 O << CurrentFnName << ":\n"; 348 349 // Number each basic block so that we can consistently refer to them 350 // in PC-relative references. 351 NumberForBB.clear(); 352 for (MachineFunction::const_iterator I = MF.begin(), E = MF.end(); 353 I != E; ++I) { 354 NumberForBB[I->getBasicBlock()] = BBNumber++; 355 } 356 357 // Print out code for the function. 358 for (MachineFunction::const_iterator I = MF.begin(), E = MF.end(); 359 I != E; ++I) { 360 // Print a label for the basic block. 361 O << ".LBB" << Mang->getValueName(MF.getFunction ()) 362 << "_" << I->getNumber () << ":\t! " 363 << I->getBasicBlock ()->getName () << "\n"; 364 for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end(); 365 II != E; ++II) { 366 // Print the assembly for the instruction. 367 O << "\t"; 368 printMachineInstruction(II); 369 } 370 } 371 372 // We didn't modify anything. 373 return false; 374} 375 376void V8Printer::printOperand(const MachineInstr *MI, int opNum) { 377 const MachineOperand &MO = MI->getOperand (opNum); 378 const MRegisterInfo &RI = *TM.getRegisterInfo(); 379 bool CloseParen = false; 380 if (MI->getOpcode() == V8::SETHIi && !MO.isRegister() && !MO.isImmediate()) { 381 O << "%hi("; 382 CloseParen = true; 383 } else if (MI->getOpcode() ==V8::ORri &&!MO.isRegister() &&!MO.isImmediate()) 384 { 385 O << "%lo("; 386 CloseParen = true; 387 } 388 switch (MO.getType()) { 389 case MachineOperand::MO_VirtualRegister: 390 if (Value *V = MO.getVRegValueOrNull()) { 391 O << "<" << V->getName() << ">"; 392 break; 393 } 394 // FALLTHROUGH 395 case MachineOperand::MO_MachineRegister: 396 if (MRegisterInfo::isPhysicalRegister(MO.getReg())) 397 O << "%" << LowercaseString (RI.get(MO.getReg()).Name); 398 else 399 O << "%reg" << MO.getReg(); 400 break; 401 402 case MachineOperand::MO_SignExtendedImmed: 403 case MachineOperand::MO_UnextendedImmed: 404 O << (int)MO.getImmedValue(); 405 break; 406 case MachineOperand::MO_MachineBasicBlock: { 407 MachineBasicBlock *MBBOp = MO.getMachineBasicBlock(); 408 O << ".LBB" << Mang->getValueName(MBBOp->getParent()->getFunction()) 409 << "_" << MBBOp->getNumber () << "\t! " 410 << MBBOp->getBasicBlock ()->getName (); 411 return; 412 } 413 case MachineOperand::MO_PCRelativeDisp: 414 std::cerr << "Shouldn't use addPCDisp() when building SparcV8 MachineInstrs"; 415 abort (); 416 return; 417 case MachineOperand::MO_GlobalAddress: 418 O << Mang->getValueName(MO.getGlobal()); 419 break; 420 case MachineOperand::MO_ExternalSymbol: 421 O << MO.getSymbolName(); 422 break; 423 case MachineOperand::MO_ConstantPoolIndex: 424 O << ".CPI" << CurrentFnName << "_" << MO.getConstantPoolIndex(); 425 break; 426 default: 427 O << "<unknown operand type>"; abort (); break; 428 } 429 if (CloseParen) O << ")"; 430} 431 432static bool isLoadInstruction (const MachineInstr *MI) { 433 switch (MI->getOpcode ()) { 434 case V8::LDSB: 435 case V8::LDSH: 436 case V8::LDUB: 437 case V8::LDUH: 438 case V8::LD: 439 case V8::LDD: 440 case V8::LDFrr: 441 case V8::LDFri: 442 case V8::LDDFrr: 443 case V8::LDDFri: 444 return true; 445 default: 446 return false; 447 } 448} 449 450static bool isStoreInstruction (const MachineInstr *MI) { 451 switch (MI->getOpcode ()) { 452 case V8::STB: 453 case V8::STH: 454 case V8::ST: 455 case V8::STD: 456 case V8::STFrr: 457 case V8::STFri: 458 case V8::STDFrr: 459 case V8::STDFri: 460 return true; 461 default: 462 return false; 463 } 464} 465 466static bool isPseudoInstruction (const MachineInstr *MI) { 467 switch (MI->getOpcode ()) { 468 case V8::PHI: 469 case V8::ADJCALLSTACKUP: 470 case V8::ADJCALLSTACKDOWN: 471 case V8::IMPLICIT_USE: 472 case V8::IMPLICIT_DEF: 473 return true; 474 default: 475 return false; 476 } 477} 478 479/// printBaseOffsetPair - Print two consecutive operands of MI, starting at #i, 480/// which form a base + offset pair (which may have brackets around it, if 481/// brackets is true, or may be in the form base - constant, if offset is a 482/// negative constant). 483/// 484void V8Printer::printBaseOffsetPair (const MachineInstr *MI, int i, 485 bool brackets) { 486 if (brackets) O << "["; 487 printOperand (MI, i); 488 if (MI->getOperand (i + 1).isImmediate()) { 489 int Val = (int) MI->getOperand (i + 1).getImmedValue (); 490 if (Val != 0) { 491 O << ((Val >= 0) ? " + " : " - "); 492 O << ((Val >= 0) ? Val : -Val); 493 } 494 } else { 495 O << " + "; 496 printOperand (MI, i + 1); 497 } 498 if (brackets) O << "]"; 499} 500 501/// printMachineInstruction -- Print out a single SparcV8 LLVM instruction 502/// MI in GAS syntax to the current output stream. 503/// 504void V8Printer::printMachineInstruction(const MachineInstr *MI) { 505 unsigned Opcode = MI->getOpcode(); 506 const TargetInstrInfo &TII = *TM.getInstrInfo(); 507 const TargetInstrDescriptor &Desc = TII.get(Opcode); 508 509 // If it's a pseudo-instruction, comment it out. 510 if (isPseudoInstruction (MI)) 511 O << "! "; 512 513 O << Desc.Name << " "; 514 515 // Printing memory instructions is a special case. 516 // for loads: %dest = op %base, offset --> op [%base + offset], %dest 517 // for stores: op %base, offset, %src --> op %src, [%base + offset] 518 if (isLoadInstruction (MI)) { 519 printBaseOffsetPair (MI, 1); 520 O << ", "; 521 printOperand (MI, 0); 522 O << "\n"; 523 return; 524 } else if (isStoreInstruction (MI)) { 525 printOperand (MI, 2); 526 O << ", "; 527 printBaseOffsetPair (MI, 0); 528 O << "\n"; 529 return; 530 } else if (Opcode == V8::JMPLrr) { 531 printBaseOffsetPair (MI, 1, false); 532 O << ", "; 533 printOperand (MI, 0); 534 O << "\n"; 535 return; 536 } 537 538 // print non-immediate, non-register-def operands 539 // then print immediate operands 540 // then print register-def operands. 541 std::vector<int> print_order; 542 for (unsigned i = 0; i < MI->getNumOperands (); ++i) 543 if (!(MI->getOperand (i).isImmediate () 544 || (MI->getOperand (i).isRegister () 545 && MI->getOperand (i).isDef ()))) 546 print_order.push_back (i); 547 for (unsigned i = 0; i < MI->getNumOperands (); ++i) 548 if (MI->getOperand (i).isImmediate ()) 549 print_order.push_back (i); 550 for (unsigned i = 0; i < MI->getNumOperands (); ++i) 551 if (MI->getOperand (i).isRegister () && MI->getOperand (i).isDef ()) 552 print_order.push_back (i); 553 for (unsigned i = 0, e = print_order.size (); i != e; ++i) { 554 printOperand (MI, print_order[i]); 555 if (i != (print_order.size () - 1)) 556 O << ", "; 557 } 558 O << "\n"; 559} 560 561bool V8Printer::doInitialization(Module &M) { 562 Mang = new Mangler(M); 563 return false; // success 564} 565 566// SwitchSection - Switch to the specified section of the executable if we are 567// not already in it! 568// 569static void SwitchSection(std::ostream &OS, std::string &CurSection, 570 const char *NewSection) { 571 if (CurSection != NewSection) { 572 CurSection = NewSection; 573 if (!CurSection.empty()) 574 OS << "\t.section \"" << NewSection << "\"\n"; 575 } 576} 577 578bool V8Printer::doFinalization(Module &M) { 579 const TargetData &TD = TM.getTargetData(); 580 std::string CurSection; 581 582 // Print out module-level global variables here. 583 for (Module::const_giterator I = M.gbegin(), E = M.gend(); I != E; ++I) 584 if (I->hasInitializer()) { // External global require no code 585 O << "\n\n"; 586 std::string name = Mang->getValueName(I); 587 Constant *C = I->getInitializer(); 588 unsigned Size = TD.getTypeSize(C->getType()); 589 unsigned Align = TD.getTypeAlignment(C->getType()); 590 591 if (C->isNullValue() && 592 (I->hasLinkOnceLinkage() || I->hasInternalLinkage() || 593 I->hasWeakLinkage() /* FIXME: Verify correct */)) { 594 SwitchSection(O, CurSection, ".data"); 595 if (I->hasInternalLinkage()) 596 O << "\t.local " << name << "\n"; 597 598 O << "\t.comm " << name << "," << TD.getTypeSize(C->getType()) 599 << "," << (unsigned)TD.getTypeAlignment(C->getType()); 600 O << "\t\t! "; 601 WriteAsOperand(O, I, true, true, &M); 602 O << "\n"; 603 } else { 604 switch (I->getLinkage()) { 605 case GlobalValue::LinkOnceLinkage: 606 case GlobalValue::WeakLinkage: // FIXME: Verify correct for weak. 607 // Nonnull linkonce -> weak 608 O << "\t.weak " << name << "\n"; 609 SwitchSection(O, CurSection, ""); 610 O << "\t.section\t\".llvm.linkonce.d." << name << "\",\"aw\",@progbits\n"; 611 break; 612 613 case GlobalValue::AppendingLinkage: 614 // FIXME: appending linkage variables should go into a section of 615 // their name or something. For now, just emit them as external. 616 case GlobalValue::ExternalLinkage: 617 // If external or appending, declare as a global symbol 618 O << "\t.globl " << name << "\n"; 619 // FALL THROUGH 620 case GlobalValue::InternalLinkage: 621 if (C->isNullValue()) 622 SwitchSection(O, CurSection, ".bss"); 623 else 624 SwitchSection(O, CurSection, ".data"); 625 break; 626 case GlobalValue::GhostLinkage: 627 std::cerr << "Should not have any unmaterialized functions!\n"; 628 abort(); 629 } 630 631 O << "\t.align " << Align << "\n"; 632 O << "\t.type " << name << ",#object\n"; 633 O << "\t.size " << name << "," << Size << "\n"; 634 O << name << ":\t\t\t\t! "; 635 WriteAsOperand(O, I, true, true, &M); 636 O << " = "; 637 WriteAsOperand(O, C, false, false, &M); 638 O << "\n"; 639 emitGlobalConstant(C); 640 } 641 } 642 643 delete Mang; 644 return false; // success 645} 646