SSATmp* IRBuilder::preOptimizeLdLocAddr(IRInstruction* inst) { auto const locId = inst->extra<LdLocAddr>()->locId; auto const type = localType(locId, DataTypeGeneric); assert(inst->typeParam().deref() >= type); inst->setTypeParam(Type::mostRefined(type.ptr(), inst->typeParam())); return nullptr; }
SSATmp* TraceBuilder::preOptimizeCheckLoc(IRInstruction* inst) { auto const locId = inst->extra<CheckLoc>()->locId; Type typeParam = inst->typeParam(); if (auto const prevValue = localValue(locId, DataTypeGeneric)) { return gen(CheckType, typeParam, inst->taken(), prevValue); } auto const prevType = localType(locId, DataTypeSpecific); if (prevType <= typeParam) { return inst->src(0); } else { // // Normally, it doesn't make sense to be checking something that's // deemed to fail. Incompatible boxed types are ok though, since // we don't track them precisely, but instead check them at every // use. // // However, in JitPGO mode right now, this pathological case can // happen, because profile counters are not accurate and we // currently don't analyze Block post-conditions when picking its // successors during region selection. This can lead to // incompatible types in blocks selected for the same region. // if (!typeParam.isBoxed() || !prevType.isBoxed()) { if ((typeParam & prevType) == Type::Bottom) { assert(RuntimeOption::EvalJitPGO); return gen(Jmp, inst->taken()); } } } return nullptr; }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = localType(locId, DataTypeGeneric); auto const typeParam = inst->typeParam(); if (prevType.not(typeParam)) { /* Task #2553746 * This is triggering for a case where the tracked state says the local is * InitNull but the AssertLoc says it's Str. */ static auto const error = makeStaticString("Internal error: static analysis was " "wrong about a local variable's type."); auto* errorInst = m_unit.gen(RaiseError, inst->marker(), cns(error)); inst->become(m_unit, errorInst); // It's not a disaster to generate this in unreachable code for // now. t2590033. if (false) { assert_log(false, [&]{ return folly::format("\npreOptimizeAssertLoc: prevType: {} " "typeParam: {}\nin instr: {}\nin trace: {}\n", prevType.toString(), typeParam.toString(), inst->toString(), m_unit.main()->toString()).str(); }); } } else if (shouldElideAssertType(prevType, typeParam, nullptr)) { // The type we're asserting is worse than the last known type. return inst->src(0); } return nullptr; }
SSATmp* IRBuilder::preOptimizeCheckLoc(IRInstruction* inst) { auto const locId = inst->extra<CheckLoc>()->locId; Type typeParam = inst->typeParam(); SSATmp* src = inst->src(0); if (auto const prevValue = localValue(locId, DataTypeGeneric)) { return gen(CheckType, typeParam, inst->taken(), prevValue); } auto const prevType = localType(locId, DataTypeGeneric); if (prevType <= typeParam) { return src; } if (prevType.not(typeParam)) { if (typeParam.isBoxed() && prevType.isBoxed()) { /* When both types are non-intersecting boxed types, we're just * updating the inner type hint. This requires no runtime work. */ constrainLocal(locId, DataTypeCountness, "preOptimizeCheckLoc"); return gen(AssertLoc, LocalId(locId), typeParam, src); } /* This check will always fail. It's probably due to an incorrect * prediction. Generate a Jmp, and return the source because * following instructions may depend on the output of CheckLoc * (they'll be DCEd later). Note that we can't use convertToJmp * because the return value isn't nullptr, so the original * instruction won't be inserted into the stream. */ gen(Jmp, inst->taken()); return src; } return nullptr; }
SSATmp* IRBuilder::preOptimizeDecRefLoc(IRInstruction* inst) { auto const locId = inst->extra<DecRefLoc>()->locId; /* * Refine the type if we can. * * We can't really rely on the types held in the boxed values since aliasing * stores may change them, and we only guard during LdRef. So we have to * change any boxed type to BoxedCell. * * DataTypeGeneric is used because we don't want a DecRef to be the only * thing keeping a guard around. This code is designed to tolerate the * incoming type being relaxed. */ auto knownType = localType(locId, DataTypeGeneric); if (knownType.isBoxed()) { knownType = Type::BoxedCell; } /* * If we have the local value in flight, use a DecRef on it instead of doing * it in memory. */ if (auto tmp = localValue(locId, DataTypeGeneric)) { gen(DecRef, tmp); inst->convertToNop(); return nullptr; } if (!typeMightRelax()) { inst->setTypeParam(std::min(knownType, inst->typeParam())); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeStLoc(IRInstruction* inst) { auto locId = inst->extra<StLoc>()->locId; auto const curType = localType(locId, DataTypeGeneric); auto const newType = inst->src(1)->type(); assert(inst->typeParam().equals(Type::None)); // There's no need to store the type if it's going to be the same // KindOfFoo. We still have to store string types because we don't // guard on KindOfStaticString vs. KindOfString. auto const bothBoxed = curType.isBoxed() && newType.isBoxed(); auto const sameUnboxed = curType != Type::None && // TODO(#2135185) curType.isSameKindOf(newType) && !curType.isString(); if (bothBoxed || sameUnboxed) { // TODO(t2598894) once relaxGuards supports proper type reflowing, we // should be able to relax the constraint here and degrade StLocNT to // StLoc if we relax its input. if (sameUnboxed) constrainLocal(locId, DataTypeSpecific, "StLoc -> StLocNT"); inst->setOpcode(StLocNT); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeDecRefLoc(IRInstruction* inst) { auto const locId = inst->extra<DecRefLoc>()->locId; /* * Refine the type if we can. * * We can't really rely on the types held in the boxed values since * aliasing stores may change them, and we only guard during LdRef. * So we have to change any boxed type to BoxedCell. */ auto knownType = localType(locId, DataTypeCountness); if (knownType.isBoxed()) { knownType = Type::BoxedCell; } if (knownType != Type::None) { // TODO(#2135185) inst->setTypeParam( Type::mostRefined(knownType, inst->typeParam()) ); } /* * If we have the local value in flight, use a DecRef on it instead * of doing it in memory. */ if (auto tmp = localValue(locId, DataTypeCountness)) { gen(DecRef, tmp); inst->convertToNop(); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = localType(locId, DataTypeGeneric); auto const typeParam = inst->typeParam(); if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { if (!prevType.subtypeOf(typeParam)) { /* Task #2553746 * This is triggering for a case where the tracked state says the local is * InitNull but the AssertLoc says it's Str. */ static auto const error = StringData::GetStaticString("Internal error: static analysis was " "wrong about a local variable's type."); auto* errorInst = m_irFactory.gen(RaiseError, inst->marker(), cns(error)); inst->become(&m_irFactory, errorInst); // It's not a disaster to generate this in unreachable code for // now. t2590033. if (false) { assert_log(false, [&]{ IRTrace& mainTrace = trace()->isMain() ? *trace() : *(trace()->main()); return folly::format("\npreOptimizeAssertLoc: prevType: {} " "typeParam: {}\nin instr: {}\nin trace: {}\n", prevType.toString(), typeParam.toString(), inst->toString(), mainTrace.toString()).str(); }); } } else { inst->convertToNop(); } } return nullptr; }
SSATmp* TraceBuilder::preOptimizeLdLocAddr(IRInstruction* inst) { auto const locId = inst->extra<LdLocAddr>()->locId; auto const type = localType(locId, DataTypeGeneric); if (!type.equals(Type::None)) { // TODO(#2135185) inst->setTypeParam(Type::mostRefined(type.ptr(), inst->typeParam())); } return nullptr; }
SSATmp* IRBuilder::preOptimizeLdLoc(IRInstruction* inst) { auto const locId = inst->extra<LdLoc>()->locId; if (auto tmp = localValue(locId, DataTypeGeneric)) { return tmp; } auto const type = localType(locId, DataTypeGeneric); // If FrameState's type isn't as good as the type param, we're missing // information in the IR. assert(inst->typeParam() >= type); inst->setTypeParam(Type::mostRefined(type, inst->typeParam())); return nullptr; }
SSATmp* IRBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; if (auto const prevValue = localValue(locId, DataTypeGeneric)) { return gen(AssertType, inst->typeParam(), prevValue); } return m_simplifier.simplifyAssertTypeOp( inst, localType(locId, DataTypeGeneric), [&](TypeConstraint tc) { constrainLocal(locId, tc, "preOptimizeAssertLoc"); } ); }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = localType(locId, DataTypeGeneric); auto const typeParam = inst->typeParam(); if (prevType.not(typeParam)) { TRACE_PUNT("Invalid AssertLoc"); } if (shouldElideAssertType(prevType, typeParam, nullptr)) { return inst->src(0); } if (filterAssertType(inst, prevType)) { constrainLocal(locId, categoryForType(prevType), "AssertLoc"); } return nullptr; }
SSATmp* IRBuilder::preOptimizeStLoc(IRInstruction* inst) { // Guard relaxation might change the current local type, so don't try to // change to StLocNT until after relaxation happens. if (typeMightRelax()) return nullptr; auto locId = inst->extra<StLoc>()->locId; auto const curType = localType(locId, DataTypeGeneric); auto const newType = inst->src(1)->type(); assert(!inst->hasTypeParam()); /* * There's no need to store the type if it's going to be the same * KindOfFoo. We'll still have to store string types because we * aren't specific about storing KindOfStaticString * vs. KindOfString, and a Type::Null might mean KindOfUninit or * KindOfNull. */ auto const bothBoxed = curType.isBoxed() && newType.isBoxed(); auto const sameUnboxed = [&] { auto avoidable = { Type::Uninit, Type::InitNull, Type::Bool, Type::Int, Type::Dbl, // No strings. Type::Arr, Type::Obj, Type::Res }; for (auto t : avoidable) { if (curType <= t && newType <= t) return true; } return false; }; if (bothBoxed || sameUnboxed()) { inst->setOpcode(StLocNT); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeStLoc(IRInstruction* inst) { // Guard relaxation might change the current local type, so don't try to // change to StLocNT until after relaxation happens. if (!inReoptimize()) return nullptr; auto locId = inst->extra<StLoc>()->locId; auto const curType = localType(locId, DataTypeGeneric); auto const newType = inst->src(1)->type(); assert(inst->typeParam() == Type::None); // There's no need to store the type if it's going to be the same // KindOfFoo. We still have to store string types because we don't // guard on KindOfStaticString vs. KindOfString. auto const bothBoxed = curType.isBoxed() && newType.isBoxed(); auto const sameUnboxed = curType.isSameKindOf(newType) && !curType.isString(); if (bothBoxed || sameUnboxed) { inst->setOpcode(StLocNT); } return nullptr; }
void CodeGenPB::go() { // first analyse the objects if they are embedded objects // for all objects that could accept such an object for(int i=0; i < m_objects.size(); i++) { // for all objects XSDObject *obj1 = m_objects.at(i); // find if there is another object that refers to the obj for(int h=0; h < m_objects.size(); h++) { XSDObject *obj2 = m_objects.at(h); // refers means obj2 has an attribute of type obj1 for(int j=0; j < obj2->attributes().size(); j++) { XSDAttribute *attr = obj2->attributes().at(j); QString objType = attr->type(); if (objType == obj1->name()) { obj1->setEmbedded(); // obj1 is embedded in obj2 } } } } // open the proto filea QString baseName; if (m_prefix != "") { baseName = m_outDir + "/" + m_prefix + ".proto"; } else { baseName = m_outDir + "/interface.proto"; // must have prefix } QFile protoFile(baseName); if (!protoFile.open(QIODevice::WriteOnly | QIODevice::Text)) { std::cerr << QString("cannot create file: %1").arg(baseName).toLatin1().data() << std::endl; std::exit(-1); } QTextStream protoFileOut(&protoFile); // GPL header protoFileOut << writeHeader( baseName ); for(int i=0; i < m_objects.size(); i++) { // get a class XSDObject *obj = m_objects.at(i); // get some vars we frequently use QString name = obj->name(); QString upperName = name.toUpper(); QVector<XSDAttribute*>attributes = obj->attributes(); QMap<QString, QString>fixedValues = obj->fixedValues(); // report std::cout << QString("creating class: %1").arg(className(name)).toLatin1().data() << std::endl; //----------------------------------------------------------------------------------------------- // generate the header //----------------------------------------------------------------------------------------------- // define the class protoFileOut << "\nmessage " << className(name) << " { \n"; if (obj->hasBaseClass()) { protoFileOut << "\nextend " << className(obj->baseClass()) << " { \n"; } // the tag counter int tag = 1; // variables section for(int j=0; j < attributes.size(); j++) { XSDAttribute *attr = attributes.at(j); QString type = localType(attr->type()); // convert to cpp types // definition if (attr->isScalar()) { // there more then one protoFileOut << " repeated " << type << " " << variableName(attr->name()) << " = " << tag++ << ";\n"; } else if (!attr->required() || obj->isMerged()) { protoFileOut << " optional " << type << " " << variableName(attr->name()) << " = " << tag++ << ";\n"; } else { protoFileOut << " required " << type << " " << variableName(attr->name()) << " = " << tag++ << ";\n"; } } // and fixed values for(int j=0; j < fixedValues.size(); j++) { QString attrName = fixedValues.keys().at(j); protoFileOut << " required string " << variableName(attrName) << " = " << tag++ << ";\n"; } protoFileOut << "}\n"; if (obj->hasBaseClass()) { protoFileOut << "}\n"; } } // close and flush protoFileOut.flush(); protoFile.close(); }