// XXX: to be refactored // This function repeats the logic in cg to pre-color tmps that are // going to be used in next native. void LinearScan::computePreColoringHint() { m_preColoringHint.clear(); IRInstruction* nextNative = getNextNative(); if (nextNative == NULL) { return; } Opcode opc = nextNative->getOpcode(); switch (opc) { case Box: if (nextNative->getSrc(0)->getType() == Type::Cell) { m_preColoringHint.add(nextNative->getSrc(0), 1, 0); } m_preColoringHint.add(nextNative->getSrc(0), 0, 1); break; case LdObjMethod: m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(0), 0, 2); break; case LdFunc: m_preColoringHint.add(nextNative->getSrc(0), 0, 1); break; case NativeImpl: m_preColoringHint.add(nextNative->getSrc(1), 0, 0); break; case DecRefLocals: case DecRefLocalsThis: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); break; case Print: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); break; case AddElem: if (nextNative->getSrc(1)->getType() == Type::Int && nextNative->getSrc(2)->getType() == Type::Int) { m_preColoringHint.add(nextNative->getSrc(0), 0, 1); m_preColoringHint.add(nextNative->getSrc(1), 0, 2); m_preColoringHint.add(nextNative->getSrc(2), 0, 3); } else { m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(2), 0, 2); m_preColoringHint.add(nextNative->getSrc(2), 1, 3); } break; case AddNewElem: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(1), 1, 2); break; case Concat: { Type::Tag lType = nextNative->getSrc(0)->getType(); Type::Tag rType = nextNative->getSrc(1)->getType(); if ((Type::isString(lType) && Type::isString(rType)) || (Type::isString(lType) && rType == Type::Int) || (lType == Type::Int && Type::isString(rType))) { m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); } else { m_preColoringHint.add(nextNative->getSrc(0), 0, 1); m_preColoringHint.add(nextNative->getSrc(1), 0, 3); } } break; case ArrayAdd: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); break; case DefFunc: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); break; case CreateCont: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(2), 0, 2); m_preColoringHint.add(nextNative->getSrc(3), 0, 3); break; case FillContLocals: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(2), 0, 2); m_preColoringHint.add(nextNative->getSrc(3), 0, 3); break; case OpEq: case OpNeq: case OpSame: case OpNSame: { auto src1 = nextNative->getSrc(0); auto src2 = nextNative->getSrc(1); auto type1 = src1->getType(); auto type2 = src2->getType(); if ((type1 == Type::Arr && type2 == Type::Arr) || (Type::isString(type1) && Type::isString(type2)) || (Type::isString(type1) && !src1->isConst()) || (type1 == Type::Obj && type2 == Type::Obj)) { m_preColoringHint.add(src1, 0, 0); m_preColoringHint.add(src2, 0, 1); } } break; case Conv: { SSATmp* src = nextNative->getSrc(0); Type::Tag toType = nextNative->getType(); Type::Tag fromType = src->getType(); if (toType == Type::Bool) { switch (fromType) { case Type::Cell: m_preColoringHint.add(src, 0, 0); m_preColoringHint.add(src, 1, 1); break; case Type::Str: case Type::StaticStr: case Type::Arr: case Type::Obj: m_preColoringHint.add(src, 0, 0); break; default: break; } } else if (Type::isString(toType)) { if (fromType == Type::Int) { m_preColoringHint.add(src, 0, 0); } } else if (Type::isString(fromType) && toType == Type::Int) { m_preColoringHint.add(src, 0, 0); } break; } default: break; } }
bool SSATmp::getValBool() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Bool)); return m_inst->extra<ConstData>()->as<bool>(); }
int64_t SSATmp::getValRawInt() const { assert(isConst()); return m_inst->extra<ConstData>()->as<int64_t>(); }
int cs6300::MultExpression::value() const { if (!isConst()) return 0; return m_lhs->value() * m_rhs->value(); }
int cs6300::AdditionExpression::value() const { if (!isConst()) return 0; return m_lhs->value() + m_rhs->value(); }
void TypeBasic::toCppMangle(OutBuffer *buf, CppMangleState *cms) { char c; char p = 0; /* ABI spec says: * v void * w wchar_t * b bool * c char * a signed char * h unsigned char * s short * t unsigned short * i int * j unsigned int * l long * m unsigned long * x long long, __int64 * y unsigned long long, __int64 * n __int128 * o unsigned __int128 * f float * d double * e long double, __float80 * g __float128 * z ellipsis * u <source-name> # vendor extended type */ switch (ty) { case Tvoid: c = 'v'; break; case Tint8: c = 'a'; break; case Tuns8: c = 'h'; break; case Tint16: c = 's'; break; case Tuns16: c = 't'; break; case Tint32: c = 'i'; break; case Tuns32: c = 'j'; break; case Tfloat32: c = 'f'; break; case Tint64: c = 'x'; break; case Tuns64: c = 'y'; break; case Tfloat64: c = 'd'; break; case Tfloat80: c = 'e'; break; case Tbool: c = 'b'; break; case Tchar: c = 'c'; break; case Twchar: c = 't'; break; case Tdchar: c = 'w'; break; case Timaginary32: p = 'G'; c = 'f'; break; case Timaginary64: p = 'G'; c = 'd'; break; case Timaginary80: p = 'G'; c = 'e'; break; case Tcomplex32: p = 'C'; c = 'f'; break; case Tcomplex64: p = 'C'; c = 'd'; break; case Tcomplex80: p = 'C'; c = 'e'; break; default: assert(0); } if (p || isConst()) { if (cms->substitute(buf, this)) return; } if (isConst()) buf->writeByte('K'); if (p) buf->writeByte(p); buf->writeByte(c); }
std::shared_ptr<cs6300::BasicBlock> cs6300::AndExpression::emit() const { if(isConst()) return LiteralExpression::emit(value(), getLabel()); return emitBinaryOp(ThreeAddressInstruction::And,getLabel(),m_lhs,m_rhs); }
int BuiltinCallNode::foldConsts(VSLDef *cdef, VSLNode** node) { // Apply standard optimization int changes = CallNode::foldConsts(cdef, node); // If optimization was a success, return if (*node != this || isConst()) return changes; // If non-associative, return if (!VSLBuiltin::isAssoc(_index)) return changes; // Otherwise: isolate constant args in constant subexpressions and // optimize them separately for (VSLNode *a = arg(); a->isListNode() && ((ListNode *)a)->tail()->isListNode(); a = ((ListNode *)a)->tail()) { ListNode *list = (ListNode *)a; ListNode *tail = (ListNode *)list->tail(); VSLNode *arg1 = list->head(); VSLNode *arg2 = tail->head(); if (arg1->isConst() && arg2->isConst()) { if (VSEFlags::show_optimize) { std::cout << "\n" << cdef->longname() << ": foldConsts: replacing\n" << *this << '\n'; std::cout.flush(); } // Found 2 args arg1, arg2 that are both constant: Replace // f(..., arg1, arg2, ...) by f(..., f(arg1, arg2), ...) // Create f(arg1, arg2) ListNode *new_args = new FixListNode(arg1, arg2); BuiltinCallNode *new_f = new BuiltinCallNode(_index, new_args); // Move nextarg into f(arg, nextarg) list->head() = new_f; list->tail() = tail->tail(); tail->head() = 0; tail->tail() = 0; delete tail; if (VSEFlags::show_optimize) { std::cout << "by " << *this << '\n'; std::cout.flush(); } changes++; } } // Now try optimization once again changes += CallNode::foldConsts(cdef, node); return changes; }
// XXX: to be refactored // This function repeats the logic in cg to pre-color tmps that are // going to be used in next native. void LinearScan::computePreColoringHint() { m_preColoringHint.clear(); IRInstruction* nextNative = getNextNative(); if (nextNative == NULL) { return; } auto normalHint = [&](int count, int srcBase = 0, int argBase = 0) { for (int i = 0; i < count; ++i) { m_preColoringHint.add(nextNative->getSrc(i + srcBase), 0, i + argBase); } }; switch (nextNative->getOpcode()) { case Box: if (nextNative->getSrc(0)->getType() == Type::Cell) { m_preColoringHint.add(nextNative->getSrc(0), 1, 0); } m_preColoringHint.add(nextNative->getSrc(0), 0, 1); break; case LdObjMethod: m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(0), 0, 2); break; case LdFunc: m_preColoringHint.add(nextNative->getSrc(0), 0, 1); break; case NativeImpl: m_preColoringHint.add(nextNative->getSrc(1), 0, 0); break; case Print: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); break; case AddElem: if (nextNative->getSrc(1)->getType() == Type::Int && nextNative->getSrc(2)->getType() == Type::Int) { normalHint(3, 0, 1); } else { m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(2), 0, 2); m_preColoringHint.add(nextNative->getSrc(2), 1, 3); } break; case AddNewElem: m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); m_preColoringHint.add(nextNative->getSrc(1), 1, 2); break; case Concat: { Type::Tag lType = nextNative->getSrc(0)->getType(); Type::Tag rType = nextNative->getSrc(1)->getType(); if ((Type::isString(lType) && Type::isString(rType)) || (Type::isString(lType) && rType == Type::Int) || (lType == Type::Int && Type::isString(rType))) { m_preColoringHint.add(nextNative->getSrc(0), 0, 0); m_preColoringHint.add(nextNative->getSrc(1), 0, 1); } else { m_preColoringHint.add(nextNative->getSrc(0), 0, 1); m_preColoringHint.add(nextNative->getSrc(1), 0, 3); } } break; case ArrayAdd: normalHint(2); break; case DefFunc: normalHint(1); break; case CreateCont: normalHint(4); break; case FillContLocals: normalHint(4); break; case OpEq: case OpNeq: case OpSame: case OpNSame: { auto src1 = nextNative->getSrc(0); auto src2 = nextNative->getSrc(1); auto type1 = src1->getType(); auto type2 = src2->getType(); if ((type1 == Type::Arr && type2 == Type::Arr) || (Type::isString(type1) && Type::isString(type2)) || (Type::isString(type1) && !src1->isConst()) || (type1 == Type::Obj && type2 == Type::Obj)) { m_preColoringHint.add(src1, 0, 0); m_preColoringHint.add(src2, 0, 1); } } break; case IterInit: { m_preColoringHint.add(nextNative->getSrc(0), 0, 1); } break; case Conv: { SSATmp* src = nextNative->getSrc(0); Type::Tag toType = nextNative->getTypeParam(); Type::Tag fromType = src->getType(); if (toType == Type::Bool) { switch (fromType) { case Type::Cell: m_preColoringHint.add(src, 0, 0); m_preColoringHint.add(src, 1, 1); break; case Type::Str: case Type::StaticStr: case Type::Arr: case Type::Obj: m_preColoringHint.add(src, 0, 0); break; default: break; } } else if (Type::isString(toType)) { if (fromType == Type::Int) { m_preColoringHint.add(src, 0, 0); } } else if (Type::isString(fromType) && toType == Type::Int) { m_preColoringHint.add(src, 0, 0); } break; } default: break; } }
void VarDeclaration::codegen(Ir* p) { Logger::print("VarDeclaration::codegen(): %s | %s\n", toChars(), type->toChars()); LOG_SCOPE; if (type->ty == Terror) { error("had semantic errors when compiling"); return; } // just forward aliases if (aliassym) { Logger::println("alias sym"); toAlias()->codegen(p); return; } // output the parent aggregate first if (AggregateDeclaration* ad = isMember()) ad->codegen(p); // global variable if (isDataseg() || (storage_class & (STCconst | STCimmutable) && init)) { Logger::println("data segment"); #if 0 // TODO: assert(!(storage_class & STCmanifest) && "manifest constant being codegen'd!"); #endif // don't duplicate work if (this->ir.resolved) return; this->ir.resolved = true; this->ir.declared = true; this->ir.irGlobal = new IrGlobal(this); Logger::println("parent: %s (%s)", parent->toChars(), parent->kind()); const bool isLLConst = isConst() && init; const llvm::GlobalValue::LinkageTypes llLinkage = DtoLinkage(this); assert(!ir.initialized); ir.initialized = gIR->dmodule; std::string llName(mangle()); // Since the type of a global must exactly match the type of its // initializer, we cannot know the type until after we have emitted the // latter (e.g. in case of unions, …). However, it is legal for the // initializer to refer to the address of the variable. Thus, we first // create a global with the generic type (note the assignment to // this->ir.irGlobal->value!), and in case we also do an initializer // with a different type later, swap it out and replace any existing // uses with bitcasts to the previous type. llvm::GlobalVariable* gvar = getOrCreateGlobal(loc, *gIR->module, i1ToI8(DtoType(type)), isLLConst, llLinkage, 0, llName, isThreadlocal()); this->ir.irGlobal->value = gvar; // Check if we are defining or just declaring the global in this module. if (!(storage_class & STCextern) && mustDefineSymbol(this)) { // Build the initializer. Might use this->ir.irGlobal->value! LLConstant *initVal = DtoConstInitializer(loc, type, init); // In case of type mismatch, swap out the variable. if (initVal->getType() != gvar->getType()->getElementType()) { llvm::GlobalVariable* newGvar = getOrCreateGlobal(loc, *gIR->module, initVal->getType(), isLLConst, llLinkage, 0, "", // We take on the name of the old global below. isThreadlocal()); newGvar->takeName(gvar); llvm::Constant* newValue = llvm::ConstantExpr::getBitCast(newGvar, gvar->getType()); gvar->replaceAllUsesWith(newValue); gvar->eraseFromParent(); gvar = newGvar; this->ir.irGlobal->value = newGvar; } // Now, set the initializer. assert(!ir.irGlobal->constInit); ir.irGlobal->constInit = initVal; gvar->setInitializer(initVal); // Also set up the edbug info. DtoDwarfGlobalVariable(gvar, this); } // Set the alignment (it is important not to use type->alignsize because // VarDeclarations can have an align() attribute independent of the type // as well). if (alignment != STRUCTALIGN_DEFAULT) gvar->setAlignment(alignment); // If this global is used from a naked function, we need to create an // artificial "use" for it, or it could be removed by the optimizer if // the only reference to it is in inline asm. if (nakedUse) gIR->usedArray.push_back(DtoBitCast(gvar, getVoidPtrType())); if (Logger::enabled()) Logger::cout() << *gvar << '\n'; } }
String ConversionOperator::basicCode(FunctionCodeScope _ref) const { return Martta::code(qualifiers() & FunctionMask) + callingCode(_ref) + (isConst() ? " const" : ""); }
void Declaration::checkModify(Loc loc, Scope *sc, Type *t) { if (sc->incontract && isParameter()) error(loc, "cannot modify parameter '%s' in contract", toChars()); if (sc->incontract && isResult()) error(loc, "cannot modify result '%s' in contract", toChars()); if (isCtorinit() && !t->isMutable()) { // It's only modifiable if inside the right constructor Dsymbol *s = sc->func; while (1) { FuncDeclaration *fd = NULL; if (s) fd = s->isFuncDeclaration(); if (fd && ((fd->isCtorDeclaration() && storage_class & STCfield) || (fd->isStaticCtorDeclaration() && !(storage_class & STCfield))) && fd->toParent() == toParent() ) { VarDeclaration *v = isVarDeclaration(); assert(v); v->ctorinit = 1; //printf("setting ctorinit\n"); } else { if (s) { s = s->toParent2(); continue; } else { const char *p = isStatic() ? "static " : ""; error(loc, "can only initialize %sconst %s inside %sconstructor", p, toChars(), p); } } break; } } else { VarDeclaration *v = isVarDeclaration(); if (v && v->canassign == 0) { const char *p = NULL; if (isConst()) p = "const"; else if (isImmutable()) p = "immutable"; else if (storage_class & STCmanifest) p = "enum"; else if (!t->isAssignable()) p = "struct with immutable members"; if (p) { error(loc, "cannot modify %s", p); } } } }
void VarDeclaration::semantic(Scope *sc) { #if 0 printf("VarDeclaration::semantic('%s', parent = '%s')\n", toChars(), sc->parent->toChars()); printf(" type = %s\n", type ? type->toChars() : "null"); printf(" stc = x%x\n", sc->stc); printf(" storage_class = x%x\n", storage_class); printf("linkage = %d\n", sc->linkage); //if (strcmp(toChars(), "mul") == 0) halt(); #endif storage_class |= sc->stc; if (storage_class & STCextern && init) error("extern symbols cannot have initializers"); /* If auto type inference, do the inference */ int inferred = 0; if (!type) { inuse++; type = init->inferType(sc); inuse--; inferred = 1; /* This is a kludge to support the existing syntax for RAII * declarations. */ storage_class &= ~STCauto; originalType = type; } else { if (!originalType) originalType = type; type = type->semantic(loc, sc); } //printf(" semantic type = %s\n", type ? type->toChars() : "null"); type->checkDeprecated(loc, sc); linkage = sc->linkage; this->parent = sc->parent; //printf("this = %p, parent = %p, '%s'\n", this, parent, parent->toChars()); protection = sc->protection; //printf("sc->stc = %x\n", sc->stc); //printf("storage_class = x%x\n", storage_class); #if DMDV2 if (storage_class & STCgshared && global.params.safe && !sc->module->safe) { error("__gshared not allowed in safe mode; use shared"); } #endif Dsymbol *parent = toParent(); FuncDeclaration *fd = parent->isFuncDeclaration(); Type *tb = type->toBasetype(); if (tb->ty == Tvoid && !(storage_class & STClazy)) { error("voids have no value"); type = Type::terror; tb = type; } if (tb->ty == Tfunction) { error("cannot be declared to be a function"); type = Type::terror; tb = type; } if (tb->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tb; if (!ts->sym->members) { error("no definition of struct %s", ts->toChars()); } } if (tb->ty == Ttuple) { /* Instead, declare variables for each of the tuple elements * and add those. */ TypeTuple *tt = (TypeTuple *)tb; size_t nelems = Parameter::dim(tt->arguments); Objects *exps = new Objects(); exps->setDim(nelems); Expression *ie = init ? init->toExpression() : NULL; for (size_t i = 0; i < nelems; i++) { Parameter *arg = Parameter::getNth(tt->arguments, i); OutBuffer buf; buf.printf("_%s_field_%zu", ident->toChars(), i); buf.writeByte(0); const char *name = (const char *)buf.extractData(); Identifier *id = Lexer::idPool(name); Expression *einit = ie; if (ie && ie->op == TOKtuple) { einit = (Expression *)((TupleExp *)ie)->exps->data[i]; } Initializer *ti = init; if (einit) { ti = new ExpInitializer(einit->loc, einit); } VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti); //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars()); v->semantic(sc); if (sc->scopesym) { //printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars()); if (sc->scopesym->members) sc->scopesym->members->push(v); } Expression *e = new DsymbolExp(loc, v); exps->data[i] = e; } TupleDeclaration *v2 = new TupleDeclaration(loc, ident, exps); v2->isexp = 1; aliassym = v2; return; } if (storage_class & STCconst && !init && !fd) // Initialize by constructor only storage_class = (storage_class & ~STCconst) | STCctorinit; if (isConst()) { } else if (isStatic()) { } else if (isSynchronized()) { error("variable %s cannot be synchronized", toChars()); } else if (isOverride()) { error("override cannot be applied to variable"); } else if (isAbstract()) { error("abstract cannot be applied to variable"); } else if (storage_class & STCtemplateparameter) { } else if (storage_class & STCctfe) { } else { AggregateDeclaration *aad = sc->anonAgg; if (!aad) aad = parent->isAggregateDeclaration(); if (aad) { #if DMDV2 assert(!(storage_class & (STCextern | STCstatic | STCtls | STCgshared))); if (storage_class & (STCconst | STCimmutable) && init) { if (!type->toBasetype()->isTypeBasic()) storage_class |= STCstatic; } else #endif aad->addField(sc, this); } InterfaceDeclaration *id = parent->isInterfaceDeclaration(); if (id) { error("field not allowed in interface"); } /* Templates cannot add fields to aggregates */ TemplateInstance *ti = parent->isTemplateInstance(); if (ti) { // Take care of nested templates while (1) { TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); if (!ti2) break; ti = ti2; } // If it's a member template AggregateDeclaration *ad = ti->tempdecl->isMember(); if (ad && storage_class != STCundefined) { error("cannot use template to add field to aggregate '%s'", ad->toChars()); } } } #if DMDV2 if ((storage_class & (STCref | STCparameter | STCforeach)) == STCref && ident != Id::This) { error("only parameters or foreach declarations can be ref"); } #endif if (type->isauto() && !noauto) { if (storage_class & (STCfield | STCout | STCref | STCstatic) || !fd) { error("globals, statics, fields, ref and out parameters cannot be auto"); } if (!(storage_class & (STCauto | STCscope))) { if (!(storage_class & STCparameter) && ident != Id::withSym) error("reference to scope class must be scope"); } } enum TOK op = TOKconstruct; if (!init && !sc->inunion && !isStatic() && !isConst() && fd && !(storage_class & (STCfield | STCin | STCforeach)) && type->size() != 0) { // Provide a default initializer //printf("Providing default initializer for '%s'\n", toChars()); if (type->ty == Tstruct && ((TypeStruct *)type)->sym->zeroInit == 1) { /* If a struct is all zeros, as a special case * set it's initializer to the integer 0. * In AssignExp::toElem(), we check for this and issue * a memset() to initialize the struct. * Must do same check in interpreter. */ Expression *e = new IntegerExp(loc, 0, Type::tint32); Expression *e1; e1 = new VarExp(loc, this); e = new AssignExp(loc, e1, e); e->op = TOKconstruct; e->type = e1->type; // don't type check this, it would fail init = new ExpInitializer(loc, e); return; } else if (type->ty == Ttypedef) { TypeTypedef *td = (TypeTypedef *)type; if (td->sym->init) { init = td->sym->init; ExpInitializer *ie = init->isExpInitializer(); if (ie) // Make copy so we can modify it init = new ExpInitializer(ie->loc, ie->exp); } else init = getExpInitializer(); } else { init = getExpInitializer(); } // Default initializer is always a blit op = TOKblit; } if (init) { sc = sc->push(); sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCref); ArrayInitializer *ai = init->isArrayInitializer(); if (ai && tb->ty == Taarray) { init = ai->toAssocArrayInitializer(); } StructInitializer *si = init->isStructInitializer(); ExpInitializer *ei = init->isExpInitializer(); // See if initializer is a NewExp that can be allocated on the stack if (ei && isScope() && ei->exp->op == TOKnew) { NewExp *ne = (NewExp *)ei->exp; if (!(ne->newargs && ne->newargs->dim)) { ne->onstack = 1; onstack = 1; if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL)) onstack = 2; } } // If inside function, there is no semantic3() call if (sc->func) { // If local variable, use AssignExp to handle all the various // possibilities. if (fd && !isStatic() && !isConst() && !init->isVoidInitializer()) { //printf("fd = '%s', var = '%s'\n", fd->toChars(), toChars()); if (!ei) { Expression *e = init->toExpression(); if (!e) { init = init->semantic(sc, type); e = init->toExpression(); if (!e) { error("is not a static and cannot have static initializer"); return; } } ei = new ExpInitializer(init->loc, e); init = ei; } Expression *e1 = new VarExp(loc, this); Type *t = type->toBasetype(); if (t->ty == Tsarray && !(storage_class & (STCref | STCout))) { ei->exp = ei->exp->semantic(sc); if (!ei->exp->implicitConvTo(type)) { int dim = ((TypeSArray *)t)->dim->toInteger(); // If multidimensional static array, treat as one large array while (1) { t = t->nextOf()->toBasetype(); if (t->ty != Tsarray) break; dim *= ((TypeSArray *)t)->dim->toInteger(); e1->type = new TypeSArray(t->nextOf(), new IntegerExp(0, dim, Type::tindex)); } } e1 = new SliceExp(loc, e1, NULL, NULL); } else if (t->ty == Tstruct) { ei->exp = ei->exp->semantic(sc); ei->exp = resolveProperties(sc, ei->exp); StructDeclaration *sd = ((TypeStruct *)t)->sym; #if DMDV2 /* Look to see if initializer is a call to the constructor */ if (sd->ctor && // there are constructors ei->exp->type->ty == Tstruct && // rvalue is the same struct ((TypeStruct *)ei->exp->type)->sym == sd && ei->exp->op == TOKstar) { /* Look for form of constructor call which is: * *__ctmp.ctor(arguments...) */ PtrExp *pe = (PtrExp *)ei->exp; if (pe->e1->op == TOKcall) { CallExp *ce = (CallExp *)pe->e1; if (ce->e1->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ce->e1; if (dve->var->isCtorDeclaration()) { /* It's a constructor call, currently constructing * a temporary __ctmp. */ /* Before calling the constructor, initialize * variable with a bit copy of the default * initializer */ Expression *e = new AssignExp(loc, new VarExp(loc, this), t->defaultInit(loc)); e->op = TOKblit; e->type = t; ei->exp = new CommaExp(loc, e, ei->exp); /* Replace __ctmp being constructed with e1 */ dve->e1 = e1; return; } } } } #endif if (!ei->exp->implicitConvTo(type)) { /* Look for opCall * See bugzilla 2702 for more discussion */ Type *ti = ei->exp->type->toBasetype(); // Don't cast away invariant or mutability in initializer if (search_function(sd, Id::call) && /* Initializing with the same type is done differently */ !(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc))) { // Rewrite as e1.call(arguments) Expression * eCall = new DotIdExp(loc, e1, Id::call); ei->exp = new CallExp(loc, eCall, ei->exp); } } } ei->exp = new AssignExp(loc, e1, ei->exp); ei->exp->op = TOKconstruct; canassign++; ei->exp = ei->exp->semantic(sc); canassign--; ei->exp->optimize(WANTvalue); } else { init = init->semantic(sc, type); if (fd && isConst() && !isStatic()) { // Make it static storage_class |= STCstatic; } } } else if (isConst() || isFinal() || parent->isAggregateDeclaration()) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() * gets ordinarily run, try to run semantic2() now. * Ignore failure. */ if (!global.errors && !inferred) { unsigned errors = global.errors; global.gag++; //printf("+gag\n"); Expression *e; Initializer *i2 = init; inuse++; if (ei) { e = ei->exp->syntaxCopy(); e = e->semantic(sc); e = e->implicitCastTo(sc, type); } else if (si || ai) { i2 = init->syntaxCopy(); i2 = i2->semantic(sc, type); } inuse--; global.gag--; //printf("-gag\n"); if (errors != global.errors) // if errors happened { if (global.gag == 0) global.errors = errors; // act as if nothing happened #if DMDV2 /* Save scope for later use, to try again */ scope = new Scope(*sc); scope->setNoFree(); #endif } else if (ei) { e = e->optimize(WANTvalue | WANTinterpret); if (e->op == TOKint64 || e->op == TOKstring || e->op == TOKfloat64) { ei->exp = e; // no errors, keep result } #if DMDV2 else { /* Save scope for later use, to try again */ scope = new Scope(*sc); scope->setNoFree(); } #endif } else init = i2; // no errors, keep result } } sc = sc->pop(); } }
int VarDeclaration::isImportedSymbol() { if (protection == PROTexport && !init && (isStatic() || isConst() || parent->isModule())) return TRUE; return FALSE; }
uintptr_t SSATmp::getValCctx() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Cctx)); return m_inst->extra<ConstData>()->as<uintptr_t>(); }
bool ModelTypeRef::match(ModelTypeRef const &typeRef) const { return(getDeclType() == typeRef.getDeclType() && isConst() == typeRef.isConst() && isRefer() == typeRef.isRefer()); }
const StringData* SSATmp::getValStr() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::StaticStr)); return m_inst->extra<ConstData>()->as<const StringData*>(); }
const ArrayData* SSATmp::getValArr() const { assert(isConst()); // TODO: Task #2124292, Reintroduce StaticArr assert(m_inst->typeParam() <= Type::Arr); return m_inst->extra<ConstData>()->as<const ArrayData*>(); }
TEST(Type, Const) { auto five = Type::cns(5); EXPECT_LT(five, Type::Int); EXPECT_NE(five, Type::Int); EXPECT_TRUE(five.isConst()); EXPECT_EQ(5, five.intVal()); EXPECT_TRUE(five.isConst(Type::Int)); EXPECT_TRUE(five.isConst(5)); EXPECT_FALSE(five.isConst(5.0)); EXPECT_TRUE(Type::Gen.maybe(five)); EXPECT_EQ(Type::Int, five | Type::Int); EXPECT_EQ(Type::Int, five | Type::cns(10)); EXPECT_EQ(five, five | Type::cns(5)); EXPECT_EQ(five, Type::cns(5) & five); EXPECT_EQ(five, five & Type::Int); EXPECT_EQ(five, Type::Gen & five); EXPECT_EQ("Int<5>", five.toString()); EXPECT_EQ(five, five - Type::Arr); EXPECT_EQ(five, five - Type::cns(1)); EXPECT_EQ(Type::Bottom, five - Type::Int); EXPECT_EQ(Type::Bottom, five - five); EXPECT_EQ(Type::Int, five.dropConstVal()); EXPECT_TRUE(five.not(Type::cns(2))); auto True = Type::cns(true); EXPECT_EQ("Bool<true>", True.toString()); EXPECT_LT(True, Type::Bool); EXPECT_NE(True, Type::Bool); EXPECT_TRUE(True.isConst()); EXPECT_EQ(true, True.boolVal()); EXPECT_TRUE(Type::Uncounted.maybe(True)); EXPECT_FALSE(five <= True); EXPECT_FALSE(five > True); EXPECT_TRUE(five.not(True)); EXPECT_EQ(Type::Int | Type::Bool, five | True); EXPECT_EQ(Type::Bottom, five & True); EXPECT_TRUE(Type::Uninit.isConst()); EXPECT_TRUE(Type::InitNull.isConst()); EXPECT_FALSE(Type::Null.isConst()); EXPECT_FALSE((Type::Uninit | Type::Bool).isConst()); EXPECT_FALSE(Type::Int.isConst()); auto array = make_packed_array(1, 2, 3, 4); auto arrData = ArrayData::GetScalarArray(array.get()); auto constArray = Type::cns(arrData); auto packedArray = Type::Arr.specialize(ArrayData::kPackedKind); auto mixedArray = Type::Arr.specialize(ArrayData::kMixedKind); EXPECT_TRUE(constArray <= packedArray); EXPECT_TRUE(constArray < packedArray); EXPECT_FALSE(packedArray <= constArray); EXPECT_TRUE(constArray <= constArray); EXPECT_FALSE(packedArray <= mixedArray); EXPECT_FALSE(mixedArray <= packedArray); EXPECT_FALSE(constArray <= mixedArray); EXPECT_EQ(constArray, constArray & packedArray); ArrayTypeTable::Builder ratBuilder; auto rat1 = ratBuilder.packedn(RepoAuthType::Array::Empty::No, RepoAuthType(RepoAuthType::Tag::Str)); auto ratArray1 = Type::Arr.specialize(rat1); auto rat2 = ratBuilder.packedn(RepoAuthType::Array::Empty::No, RepoAuthType(RepoAuthType::Tag::Int)); auto ratArray2 = Type::Arr.specialize(rat2); EXPECT_EQ(Type::Arr, ratArray1 & ratArray2); EXPECT_TRUE(ratArray1 < Type::Arr); EXPECT_TRUE(ratArray1 <= ratArray1); EXPECT_TRUE(ratArray1 < (Type::Arr|Type::Obj)); EXPECT_FALSE(ratArray1 < ratArray2); EXPECT_NE(ratArray1, ratArray2); auto packedRat = packedArray & ratArray1; EXPECT_EQ("Arr<PackedKind:N([Str])>", packedRat.toString()); EXPECT_TRUE(packedRat <= packedArray); EXPECT_TRUE(packedRat < packedArray); EXPECT_TRUE(packedRat <= ratArray1); EXPECT_TRUE(packedRat < ratArray1); EXPECT_EQ(packedRat, packedRat & packedArray); EXPECT_EQ(packedRat, packedRat & ratArray1); }
const Func* SSATmp::getValFunc() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Func)); return m_inst->extra<ConstData>()->as<const Func*>(); }
int cs6300::AndExpression::value() const { if (!isConst()) return 0; return m_lhs->value() && m_rhs->value(); }
const Class* SSATmp::getValClass() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Cls)); return m_inst->extra<ConstData>()->as<const Class*>(); }
bool Argument:: isConstRef() const { return isConst() && refType_ == RefType::LValue; }
const NamedEntity* SSATmp::getValNamedEntity() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::NamedEntity)); return m_inst->extra<ConstData>()->as<const NamedEntity*>(); }
bool Type::subtypeOf(Type t2) const { // First, check for any members in m_bits that aren't in t2.m_bits. if ((m_bits & t2.m_bits) != m_bits) return false; // If t2 is a constant, we must be the same constant or Bottom. if (t2.m_hasConstVal) { assert(!t2.isUnion()); return m_bits == kBottom || (m_hasConstVal && m_extra == t2.m_extra); } // If t2 is specialized, we must either not be eligible for the same kind of // specialization (Int <= {Int|Arr<Packed>}) or have a specialization that is // a subtype of t2's specialization. if (t2.isSpecialized()) { if (t2.canSpecializeClass()) { if (!isSpecialized()) return false; // Obj=A <: Obj=A // Obj<=A <: Obj<=A if (m_class.isExact() == t2.m_class.isExact() && getClass() == t2.getClass()) { return true; } // A <: B // ---------------- // Obj=A <: Obj<=B // Obj<=A <: Obj<=B if (!t2.m_class.isExact()) return getClass()->classof(t2.getClass()); return false; } assert(t2.canSpecializeArray()); if (!canSpecializeArray()) return true; if (!isSpecialized()) return false; // Both types are specialized Arr types. "Specialized" in this context // means it has at least one of a RepoAuthType::Array* or (const ArrayData* // or ArrayData::ArrayKind). We may return false erroneously in cases where // a 100% accurate comparison of the specializations would be prohibitively // expensive. if (m_arrayInfo == t2.m_arrayInfo) return true; auto rat1 = getArrayType(); auto rat2 = t2.getArrayType(); if (rat1 != rat2 && !(rat1 && !rat2)) { // Different rats are only ok if rat1 is present and rat2 isn't. It's // possible for one rat to be a subtype of another rat or array kind, but // checking that can be very expensive. return false; } auto kind1 = getOptArrayKind(); auto kind2 = t2.getOptArrayKind(); assert(kind1 || kind2); if (kind1 && !kind2) return true; if (kind2 && !kind1) return false; if (*kind1 != *kind2) return false; // Same kinds but we still have to check for const arrays. a <= b iff they // have the same const array or a has a const array and b doesn't. If they // have the same non-nullptr const array the m_arrayInfo check up above // should've triggered. auto const1 = isConst() ? arrVal() : nullptr; auto const2 = t2.isConst() ? t2.arrVal() : nullptr; assert((!const1 && !const2) || const1 != const2); return const1 == const2 || (const1 && !const2); } return true; }
uintptr_t SSATmp::getValBits() const { assert(isConst()); return m_inst->extra<ConstData>()->as<uintptr_t>(); }
int64_t SSATmp::getValInt() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Int)); return m_inst->extra<ConstData>()->as<int64_t>(); }
TCA SSATmp::getValTCA() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::TCA)); return m_inst->extra<ConstData>()->as<TCA>(); }
double SSATmp::getValDbl() const { assert(isConst()); assert(m_inst->typeParam().equals(Type::Dbl)); return m_inst->extra<ConstData>()->as<double>(); }
int cs6300::NeqExpression::value() const { if (!isConst()) return 0; return m_lhs->value() != m_rhs->value(); }