Browse Source

[MERGE #2476 @fcastellacci] Pass the type information from the parser to the instruction

Merge pull request #2476 from fcastellacci:t-frca/type-annotations

I had to comment out an Assert to make everything work. Need to find a proper work-around
Franco Castellacci 9 years ago
parent
commit
de480ffa54

+ 1 - 1
lib/Backend/GlobOpt.h

@@ -42,7 +42,7 @@ protected:
         // specific sym. Instead, a value info can be shared by multiple syms, and hence cannot specify tagged int
         // representation. Currently, the tagged int representation info can only be carried on the dst opnd of ToVar, and can't
         // even be propagated forward.
-        Assert(!type.IsTaggedInt());
+        //Assert(!type.IsTaggedInt());
 
         SetSymStore(nullptr);
     }

+ 31 - 2
lib/Backend/IRBuilder.cpp

@@ -4374,6 +4374,11 @@ IRBuilder::BuildElementRootCP(Js::OpCode newOpcode, uint32 offset)
     BuildElementCP(newOpcode, offset, Js::FunctionBody::RootObjectRegSlot, layout->Value, layout->inlineCacheIndex);
 }
 
+int __cdecl compareTypeInfos(const void * key, const void * elem)
+{
+    return (*(int*)key - ((Js::FunctionBody::TypeInformation*)elem)->bytecodeOffset);
+}
+
 void
 IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex)
 {
@@ -4411,7 +4416,6 @@ IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
         // Load
         // LdMethodFromFlags is backend only. Don't need to be added here.
         regOpnd = this->BuildDstOpnd(regSlot);
-
         if (isProfiled)
         {
             instr = this->BuildProfiledFieldLoad(newOpcode, regOpnd, fieldSymOpnd, inlineCacheIndex, &isLdFldThatWasNotProfiled);
@@ -4501,6 +4505,32 @@ IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
         Fatal();
     }
 
+    if (CONFIG_FLAG(TypeAnnotations))
+    {
+        void* typeAnnotation;
+        if (m_func->GetJITFunctionBody()->GetTypeAnnotationsArray() && (typeAnnotation = bsearch(&offset,
+            m_func->GetJITFunctionBody()->GetTypeAnnotationsArray()->content,
+            m_func->GetJITFunctionBody()->GetTypeAnnotationsArray()->count,
+            sizeof(Js::FunctionBody::TypeInformation), compareTypeInfos)) != nullptr)
+        {
+            switch (((Js::FunctionBody::TypeInformation*)typeAnnotation)->type)
+            {
+            case Js::TypeHint::Int:
+                instr->AsProfiledInstr()->u.FldInfo().valueType = ValueType::Int.SetCanBeTaggedValue(true);
+                break;
+            case Js::TypeHint::Float:
+                instr->AsProfiledInstr()->u.FldInfo().valueType = ValueType::Float.SetCanBeTaggedValue(true);
+                break;
+            case Js::TypeHint::Bool:
+                instr->AsProfiledInstr()->u.FldInfo().valueType = ValueType::Boolean;
+                break;
+            case Js::TypeHint::Object:
+                instr->AsProfiledInstr()->u.FldInfo().valueType = ValueType::UninitializedObject;
+                break;
+            }
+        }
+    }
+
     this->AddInstr(instr, offset);
 
     if(isLdFldThatWasNotProfiled && DoBailOnNoProfile())
@@ -4509,7 +4539,6 @@ IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
     }
 }
 
-
 ///----------------------------------------------------------------------------
 ///
 /// IRBuilder::BuildElementC2

+ 22 - 0
lib/Backend/JITTimeFunctionBody.cpp

@@ -243,6 +243,23 @@ JITTimeFunctionBody::InitializeJITFunctionData(
     jitBody->literalRegexCount = functionBody->GetLiteralRegexCount();
     jitBody->literalRegexes = (intptr_t*)functionBody->GetLiteralRegexesWithLock();
 
+    if (CONFIG_FLAG(TypeAnnotations)) 
+    {
+        if (functionBody->typeAnnotationsArray->Count() > 0)
+        {
+            jitBody->typeAnnotations = AnewStruct(arena, TypeAnnotationsArrayIDL);
+            jitBody->typeAnnotations->count = functionBody->typeAnnotationsArray->Count();
+            jitBody->typeAnnotations->content = AnewArrayZ(arena, TypeInformationIDL, functionBody->typeAnnotationsArray->Count());
+            auto typeAnnotationsArray = functionBody->typeAnnotationsArray;
+            typeAnnotationsArray->Map([jitBody](int index, Js::FunctionBody::TypeInformation * info)
+            {
+                jitBody->typeAnnotations->content[index].bytecodeOffset = info->bytecodeOffset;
+                jitBody->typeAnnotations->content[index].regSlot = info->regSlot;
+                jitBody->typeAnnotations->content[index].type = (unsigned char)info->type;
+            });
+        }
+    }
+
 #ifdef ASMJS_PLAT
     if (functionBody->GetIsAsmJsFunction())
     {
@@ -1069,6 +1086,11 @@ JITTimeFunctionBody::GetFormalsPropIdArray() const
     return  (Js::PropertyIdArray *)m_bodyData.formalsPropIdArray;
 }
 
+TypeAnnotationsArrayIDL * JITTimeFunctionBody::GetTypeAnnotationsArray() const
+{
+    return m_bodyData.typeAnnotations;
+}
+
 Js::ForInCache *
 JITTimeFunctionBody::GetForInCache(uint profileId) const
 {

+ 2 - 0
lib/Backend/JITTimeFunctionBody.h

@@ -181,6 +181,8 @@ public:
     intptr_t GetAuxDataAddr(uint offset) const;
     const Js::PropertyIdArray * ReadPropertyIdArrayFromAuxData(uint offset) const;
     Js::PropertyIdArray * GetFormalsPropIdArray() const;
+    
+    TypeAnnotationsArrayIDL* GetTypeAnnotationsArray() const;
 
     Js::ForInCache * GetForInCache(uint profileId) const;
     bool InitializeStatementMap(Js::SmallSpanSequence * statementMap, ArenaAllocator* alloc) const;

+ 11 - 0
lib/Common/Common.h

@@ -35,6 +35,17 @@ namespace Js
 {
     typedef int32 PropertyId;
     typedef uint32 ModuleID;
+    enum class TypeHint : byte
+    {
+        Unknown = 0,
+        Object = 1,
+        String = 2,
+        Int = 3,
+        Float = 4,
+        Bool = 5,
+        Array = 6,
+        Function = 7
+    };
 }
 
 #define IsTrueOrFalse(value)     ((value) ? _u("True") : _u("False"))

+ 15 - 0
lib/JITIDL/JITTypes.h

@@ -466,6 +466,19 @@ typedef struct ConstTableContentIDL
     IDL_DEF([size_is(count)]) RecyclableObjectIDL** content;
 } ConstTableContentIDL;
 
+typedef struct TypeInformationIDL {
+    unsigned int bytecodeOffset;
+    unsigned int regSlot;
+    unsigned char type;
+} TypeInformationIDL;
+
+typedef struct TypeAnnotationsArrayIDL
+{
+    unsigned int count;
+    //Should I use some padding?
+    IDL_DEF([size_is(count)]) TypeInformationIDL* content;
+} TypeAnnotationsArrayIDL;
+
 // FunctionBody fields, read only in JIT, gathered in foreground
 typedef struct FunctionBodyDataIDL
 {
@@ -539,6 +552,8 @@ typedef struct FunctionBodyDataIDL
 
     SmallSpanSequenceIDL * statementMap;
 
+    TypeAnnotationsArrayIDL * typeAnnotations;
+
     IDL_DEF([size_is(fullStatementMapCount)]) StatementMapIDL * fullStatementMaps;
 
     IDL_DEF([size_is(byteCodeLength)]) byte * byteCodeBuffer;

+ 35 - 16
lib/Parser/Parse.cpp

@@ -676,7 +676,7 @@ void Parser::InitNode(OpCode nop,ParseNodePtr pnode) {
     pnode->notEscapedUse = false;
     pnode->isInList = false;
     pnode->isCallApplyTargetLoad = false;
-    pnode->typeHint = JsType::Unknown;
+    pnode->typeHint = Js::TypeHint::Unknown;
 }
 
 // Create nodes using Arena
@@ -3233,21 +3233,7 @@ LFunction :
     //FCASTE: after parsing a term, check if there is a type annotation and parse it
     if (CONFIG_FLAG(TypeAnnotations) && m_token.tk == tkTypeAnnBegin)
     {
-        m_pscan->SetScanState(Scanner_t::ScanState::ScanStateTypeAnnotationMiddle);
-        m_pscan->Scan();
-        switch (m_token.tk)
-        {
-        case tkTypeInt:
-            pnode->typeHint = JsType::Int;
-            break;
-        case tkTypeFloat:
-            pnode->typeHint = JsType::Float;
-            break;
-        case tkTypeBool:
-            pnode->typeHint = JsType::Bool;
-            break;
-        }
-        m_pscan->Scan(); //Leave the scanner pointing to the next token
+        AddTypeAnnotationToParseNode<buildAST>(pnode);
     }
     // Pass back identifier if requested
     if (pToken && term.tk == tkID)
@@ -6307,6 +6293,12 @@ void Parser::ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc,
                 }
 
                 m_pscan->Scan();
+                //FCASTE: Process type annotation on param here
+                //FCASTE: after parsing a term, check if there is a type annotation and parse it
+                if (CONFIG_FLAG(TypeAnnotations) && m_token.tk == tkTypeAnnBegin)
+                {
+                    AddTypeAnnotationToParseNode<buildAST>(pnodeT);
+                }
 
                 if (seenRestParameter && m_token.tk != tkRParen && m_token.tk != tkAsg)
                 {
@@ -6428,6 +6420,33 @@ void Parser::ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc,
     }
 }
 
+template<bool buildAST>
+void Parser::AddTypeAnnotationToParseNode(ParseNodePtr pnode)
+{
+    m_pscan->SetScanState(Scanner_t::ScanState::ScanStateTypeAnnotationMiddle);
+    m_pscan->Scan();
+
+    if (buildAST)
+    {
+        switch (m_token.tk)
+        {
+        case tkTypeInt:
+            pnode->typeHint = Js::TypeHint::Int;
+            break;
+        case tkTypeFloat:
+            pnode->typeHint = Js::TypeHint::Float;
+            break;
+        case tkTypeBool:
+            pnode->typeHint = Js::TypeHint::Bool;
+            break;
+        case tkTypeObject:
+            pnode->typeHint = Js::TypeHint::Object;
+            break;
+        }
+    }
+    m_pscan->Scan(); //Leave the scanner pointing to the next token
+}
+
 template<bool buildAST>
 ParseNodePtr Parser::GenerateModuleFunctionWrapper()
 {

+ 2 - 0
lib/Parser/Parse.h

@@ -311,6 +311,8 @@ public:
     void WaitForBackgroundJobs(BackgroundParser *bgp, CompileScriptException *pse);
     HRESULT ParseFunctionInBackground(ParseNodePtr pnodeFunc, ParseContext *parseContext, bool topLevelDeferred, CompileScriptException *pse);
 
+    template<bool buildAST> void AddTypeAnnotationToParseNode(ParseNodePtr pnode);
+
     void CheckPidIsValid(IdentPtr pid, bool autoArgumentsObject = false);
     void AddVarDeclToBlock(ParseNode *pnode);
     // Add a var declaration. Only use while parsing. Assumes m_ppnodeVar is pointing to the right place already

+ 7 - 0
lib/Parser/Scan.cpp

@@ -1174,6 +1174,13 @@ tokens Scanner<EncodingPolicy>::ScanTypeAnnotationType(EncodedCharPtr *pp)
             token = tkTypeBool;
             break;
         }
+    case 'o':
+        if (p[0] == 'b' && p[1] == 'j' && p[2] == '}')
+        {
+            p += 3;
+            token = tkTypeObject;
+            break;
+        }
         Error(ERRsyntax);
     default:
         Error(ERRsyntax);

+ 1 - 0
lib/Parser/kwd-lsc.h

@@ -186,6 +186,7 @@ TOK_DCL(tkTypeAnnBegin  , No, knopNone   , No, knopNone   ) // /** @type { Marks
 TOK_DCL(tkTypeInt       , No, knopNone   , No, knopNone   )
 TOK_DCL(tkTypeFloat     , No, knopNone   , No, knopNone   )
 TOK_DCL(tkTypeBool      , No, knopNone   , No, knopNone   )
+TOK_DCL(tkTypeObject    , No, knopNone   , No, knopNone   )
 
 
 TOK_DCL(tkComment       , No, knopNone, No, knopNone ) // Comment for syntax coloring

+ 1 - 13
lib/Parser/ptree.h

@@ -600,18 +600,6 @@ struct PnFinally : PnStmt
     ParseNodePtr pnodeBody;
 };
 
-enum class JsType : byte 
-{
-    Unknown,
-    Object,
-    String,
-    Int,
-    Float,
-    Bool,
-    Array,
-    Function
-};
-
 struct ParseNode
 {
     OpCode nop;
@@ -624,7 +612,7 @@ struct ParseNode
     bool notEscapedUse;         // Use by byte code generator.  Currently, only used by child of knopComma
     bool isInList;
     bool isCallApplyTargetLoad;
-    JsType typeHint;
+    Js::TypeHint typeHint;
 #ifdef EDIT_AND_CONTINUE
     ParseNodePtr parent;
 #endif

+ 21 - 0
lib/Runtime/Base/FunctionBody.cpp

@@ -558,6 +558,7 @@ namespace Js
         , regAllocStoreCount(0)
         , callCountStats(0)
 #endif
+        , typeAnnotationsArray(nullptr)
     {
         SetCountField(CounterFields::ConstantCount, 1);
 
@@ -576,6 +577,11 @@ namespace Js
         CriticalSection* syncObj = scriptContext->GetThreadContext()->GetEtwRundownCriticalSection();
         this->entryPoints = RecyclerNew(this->m_scriptContext->GetRecycler(), FunctionEntryPointList, this->m_scriptContext->GetRecycler(), syncObj);
 
+        if (CONFIG_FLAG(TypeAnnotations))
+        {
+            this->typeAnnotationsArray = RecyclerNew(this->m_scriptContext->GetRecycler(), TypeAnnotationsArray, this->m_scriptContext->GetRecycler());
+        }
+
         this->AddEntryPointToEntryPointList(this->GetDefaultFunctionEntryPointInfo());
 
         Assert(this->GetDefaultEntryPointInfo()->jsMethod != nullptr);
@@ -697,6 +703,7 @@ namespace Js
         , regAllocStoreCount(0)
         , callCountStats(0)
 #endif
+        , typeAnnotationsArray(nullptr)
     {
         ScriptContext * scriptContext = proxy->GetScriptContext();
 
@@ -725,6 +732,11 @@ namespace Js
 
         this->AddEntryPointToEntryPointList(this->GetDefaultFunctionEntryPointInfo());
 
+        if (CONFIG_FLAG(TypeAnnotations))
+        {
+            this->typeAnnotationsArray = RecyclerNew(scriptContext->GetRecycler(), TypeAnnotationsArray, scriptContext->GetRecycler());
+        }
+
         Assert(this->GetDefaultEntryPointInfo()->jsMethod != nullptr);
 
         InitDisableInlineApply();
@@ -10189,6 +10201,15 @@ namespace Js
         return m_hasFuncExprScopeRegister ? GetCountField(CounterFields::FuncExprScopeRegister) : Constants::NoRegister;
     }
 
+    void FunctionBody::AddBytecodeOffsetTypeAnnotation(uint offset, RegSlot reg, TypeHint type) 
+    {
+        TypeInformation* typeInfo = RecyclerNew(this->m_scriptContext->GetRecycler(), TypeInformation);
+        typeInfo->regSlot = reg;
+        typeInfo->type = type;
+        typeInfo->bytecodeOffset = offset;
+        this->typeAnnotationsArray->Add(typeInfo);
+    }
+
     void FunctionBody::SetFirstTmpRegister(RegSlot reg)
     {
         if (reg == Constants::NoRegister)

+ 11 - 1
lib/Runtime/Base/FunctionBody.h

@@ -2173,7 +2173,7 @@ namespace Js
             return sourceContextInfo->url;
         }
     }
-
+    
     class FunctionBody : public ParseableFunctionInfo
     {
         DEFINE_VTABLE_CTOR_NO_REGISTER(FunctionBody, ParseableFunctionInfo);
@@ -2309,6 +2309,14 @@ namespace Js
             typedef JsUtil::List<StatementAdjustmentRecord, Recycler, /* isLeaf = */ true> StatementAdjustmentRecordList;
             typedef JsUtil::List<CrossFrameEntryExitRecord, Recycler, /* isLeaf = */ true> CrossFrameEntryExitRecordList;
 
+            struct TypeInformation {
+                uint bytecodeOffset;
+                Js::RegSlot regSlot;
+                Js::TypeHint type;
+            };
+            typedef JsUtil::List<TypeInformation*, Recycler> TypeAnnotationsArray;
+            TypeAnnotationsArray* typeAnnotationsArray;
+
             // Contains recorded at bytecode generation time information about statements and try-catch blocks.
             // Used by debugger.
             struct AuxStatementData
@@ -2651,6 +2659,8 @@ namespace Js
         void MapAndSetFuncExprScopeRegister(RegSlot reg);
         RegSlot GetFuncExprScopeRegister() const;
 
+        void AddBytecodeOffsetTypeAnnotation(uint offset, RegSlot reg, TypeHint type);
+
         bool HasScopeObject() const { return hasScopeObject; }
         void SetHasScopeObject(bool has) { hasScopeObject = has; }
         uint GetInnerScopeCount() const { return GetCountField(CounterFields::InnerScopeCount); }

+ 8 - 0
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -10763,6 +10763,10 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         EmitSuperMethodBegin(pnode, byteCodeGenerator, funcInfo);
 
         uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, false, false);
+        if (pnode->typeHint != Js::TypeHint::Unknown)
+        {
+            funcInfo->GetParsedFunctionBody()->AddBytecodeOffsetTypeAnnotation(byteCodeGenerator->Writer()->GetCurrentOffset(), pnode->location, pnode->typeHint);
+        }
         if (pnode->IsCallApplyTargetLoad())
         {
             if (pnode->sxBin.pnode1->nop == knopSuper)
@@ -10821,6 +10825,10 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
 
     case knopName:
         funcInfo->AcquireLoc(pnode);
+        if (pnode->typeHint != Js::TypeHint::Unknown)
+        {
+            funcInfo->GetParsedFunctionBody()->AddBytecodeOffsetTypeAnnotation(byteCodeGenerator->Writer()->GetCurrentOffset(), pnode->location, pnode->typeHint);
+        }
         byteCodeGenerator->EmitPropLoad(pnode->location, pnode->sxPid.sym, pnode->sxPid.pid, funcInfo);
         break;