1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
17 #include "assembler.h"
19 #include "coreclrloader.h"
20 CoreCLRLoader *g_loader;
22 MetaDataGetDispenserFunc metaDataGetDispenser;
24 void indexKeywords(Indx* indx); // defined in asmparse.y
26 unsigned int g_uCodePage = CP_ACP;
27 unsigned int g_uConsoleCP = CP_ACP;
29 char g_szSourceFileName[MAX_FILENAME_LENGTH*3];
31 WCHAR wzUniBuf[dwUniBuf]; // Unicode conversion global buffer
33 Assembler::Assembler()
41 char* pszFQN = new char[16];
42 strcpy_s(pszFQN,16,"<Module>");
43 m_pModuleClass = new Class(pszFQN);
44 m_lstClass.PUSH(m_pModuleClass);
45 m_hshClass.PUSH(m_pModuleClass);
46 m_pModuleClass->m_cl = mdTokenNil;
47 m_pModuleClass->m_bIsMaster = FALSE;
49 m_fStdMapping = FALSE;
50 m_fDisplayTraceOutput= FALSE;
52 m_fTolerateDupMethods = FALSE;
54 m_pCurOutputPos = NULL;
56 m_CurPC = 0; // PC offset in method
62 m_wzMetadataVersion = NULL;
66 m_wSSVersionMajor = 4;
67 m_wSSVersionMinor = 0;
68 m_fAppContainer = FALSE;
69 m_fHighEntropyVA = FALSE;
76 m_pCustomDescrList = NULL;
78 m_pGlobalDataSection = NULL;
82 m_fDidCoInitialise = FALSE;
85 m_fEntryPointPresent = FALSE;
86 m_fHaveFieldsWithRvas = FALSE;
88 m_dwMethodsFolded = 0;
91 m_crExtends = mdTypeDefNil;
97 m_firstArgName = NULL;
99 m_szNamespace = new char[2];
100 m_szNamespace[0] = 0;
101 m_NSstack.PUSH(m_szNamespace);
103 m_szFullNS = new char[MAX_NAMESPACE_LENGTH];
104 memset(m_szFullNS,0,MAX_NAMESPACE_LENGTH);
105 m_ulFullNSLen = MAX_NAMESPACE_LENGTH;
108 m_fInitialisedMetaData = FALSE;
109 m_fAutoInheritFromObject = TRUE;
111 m_ulLastDebugLine = 0xFFFFFFFF;
112 m_ulLastDebugColumn = 0xFFFFFFFF;
113 m_ulLastDebugLineEnd = 0xFFFFFFFF;
114 m_ulLastDebugColumnEnd = 0xFFFFFFFF;
116 m_pSymDocument = NULL;
117 m_dwIncludeDebugInfo = 0;
118 m_fGeneratePDB = FALSE;
119 m_fIsMscorlib = FALSE;
130 m_fReportProgress = TRUE;
131 m_tkCurrentCVOwner = 1; // module
132 m_pOutputBuffer = NULL;
134 m_dwSubsystem = (DWORD)-1;
135 m_dwComImageFlags = COMIMAGE_FLAGS_ILONLY;
136 m_dwFileAlignment = 0;
138 m_stSizeOfStackReserve = 0;
139 m_dwCeeFileFlags = ICEE_CREATE_FILE_PURE_IL;
141 g_szSourceFileName[0] = 0;
143 m_guidLang = CorSym_LanguageType_ILAssembly;
144 m_guidLangVendor = CorSym_LanguageVendor_Microsoft;
145 m_guidDoc = CorSym_DocumentType_Text;
146 for(int i=0; i<INSTR_POOL_SIZE; i++) m_Instr[i].opcode = -1;
147 m_wzResourceFile = NULL;
148 m_wzKeySourceName = NULL;
154 m_pOutputBuffer = new BYTE[OUTPUT_BUFFER_SIZE];
156 m_pCurOutputPos = m_pOutputBuffer;
157 m_pEndOutputPos = m_pOutputBuffer + OUTPUT_BUFFER_SIZE;
159 m_crImplList = new mdTypeRef[MAX_INTERFACES_IMPLEMENTED];
160 m_nImplListSize = MAX_INTERFACES_IMPLEMENTED;
162 m_pManifest = new AsmMan((void*)this);
164 dummyClass = new Class(NULL);
165 indexKeywords(&indxKeywords);
169 Assembler::~Assembler()
171 if(m_pbsMD) delete m_pbsMD;
173 if(m_pMarshal) delete m_pMarshal;
174 if(m_pManifest) delete m_pManifest;
175 if(m_pPInvoke) delete m_pPInvoke;
177 if(m_pVTable) delete m_pVTable;
179 m_lstGlobalLabel.RESET(true);
180 m_lstGlobalFixup.RESET(true);
181 m_hshClass.RESET(false);
182 m_lstClass.RESET(true);
183 while((m_ClassStack.POP()));
184 while(m_CustomDescrListStack.POP());
186 dummyClass->m_szFQN = NULL;
189 if (m_pOutputBuffer) delete [] m_pOutputBuffer;
190 if (m_crImplList) delete [] m_crImplList;
191 if (m_TyParList) delete m_TyParList;
193 if (m_pCeeFileGen != NULL) {
195 m_pCeeFileGen->DestroyCeeFile(&m_pCeeFile);
197 DestroyICeeFileGen(&m_pCeeFileGen);
199 m_pCeeFileGen = NULL;
202 while((m_szNamespace = m_NSstack.POP())) ;
203 delete [] m_szFullNS;
205 m_DocWriterList.RESET(true);
207 m_MethodBodyList.RESET(true);
209 m_TypeDefDList.RESET(true);
211 if (m_pSymWriter != NULL)
213 m_pSymWriter->Close();
214 m_pSymWriter->Release();
217 if (m_pImporter != NULL)
219 m_pImporter->Release();
222 if (m_pEmitter != NULL)
224 m_pEmitter->Release();
235 if (g_loader != NULL)
244 BOOL Assembler::Init()
247 g_loader = CoreCLRLoader::Create(g_pszExeFile);
248 if (g_loader == NULL)
252 metaDataGetDispenser = (MetaDataGetDispenserFunc)g_loader->LoadFunction("MetaDataGetDispenser");
254 metaDataGetDispenser = (MetaDataGetDispenserFunc)MetaDataGetDispenser;
255 #endif // FEATURE_PAL
256 if (m_pCeeFileGen != NULL) {
258 m_pCeeFileGen->DestroyCeeFile(&m_pCeeFile);
260 DestroyICeeFileGen(&m_pCeeFileGen);
262 m_pCeeFileGen = NULL;
265 if (FAILED(CreateICeeFileGen(&m_pCeeFileGen))) return FALSE;
267 if (FAILED(m_pCeeFileGen->CreateCeeFileEx(&m_pCeeFile,(ULONG)m_dwCeeFileFlags))) return FALSE;
269 if (FAILED(m_pCeeFileGen->GetSectionCreate(m_pCeeFile, ".il", sdReadOnly, &m_pILSection))) return FALSE;
270 if (FAILED(m_pCeeFileGen->GetSectionCreate (m_pCeeFile, ".sdata", sdReadWrite, &m_pGlobalDataSection))) return FALSE;
271 if (FAILED(m_pCeeFileGen->GetSectionCreate (m_pCeeFile, ".tls", sdReadWrite, &m_pTLSSection))) return FALSE;
276 void Assembler::SetDLL(BOOL IsDll)
279 OK = m_pCeeFileGen->SetDllSwitch(m_pCeeFile, IsDll);
280 _ASSERTE(SUCCEEDED(OK));
285 void Assembler::SetOBJ(BOOL IsObj)
288 OK = m_pCeeFileGen->SetObjSwitch(m_pCeeFile, IsObj);
289 _ASSERTE(SUCCEEDED(OK));
295 void Assembler::ResetArgNameList()
297 if(m_firstArgName) delArgNameList(m_firstArgName);
298 m_firstArgName = NULL;
299 m_lastArgName = NULL;
302 void Assembler::ResetForNextMethod()
308 m_pCurOutputPos = m_pOutputBuffer;
313 void Assembler::ResetLineNumbers()
315 // reset line number information
316 m_ulLastDebugLine = 0xFFFFFFFF;
317 m_ulLastDebugColumn = 0xFFFFFFFF;
318 m_ulLastDebugLineEnd = 0xFFFFFFFF;
319 m_ulLastDebugColumnEnd = 0xFFFFFFFF;
322 BOOL Assembler::AddMethod(Method *pMethod)
324 BOOL fIsInterface=FALSE, fIsImport=FALSE;
325 ULONG PEFileOffset=0;
327 _ASSERTE(m_pCeeFileGen != NULL);
330 report->error("pMethod == NULL");
333 if(pMethod->m_pClass != NULL)
335 fIsInterface = IsTdInterface(pMethod->m_pClass->m_Attr);
336 fIsImport = IsTdImport(pMethod->m_pClass->m_Attr);
342 if(fIsImport) strcat_s(sz,1024," imported");
343 if(IsMdAbstract(pMethod->m_Attr)) strcat_s(sz,1024," abstract");
344 if(IsMdPinvokeImpl(pMethod->m_Attr)) strcat_s(sz,1024," pinvoke");
345 if(!IsMiIL(pMethod->m_wImplAttr)) strcat_s(sz,1024," non-IL");
346 if(IsMiRuntime(pMethod->m_wImplAttr)) strcat_s(sz,1024," runtime-supplied");
347 if(IsMiInternalCall(pMethod->m_wImplAttr)) strcat_s(sz,1024," an internal call");
350 report->error("Method cannot have body if it is%s\n",sz);
353 else // method has no body
355 if(fIsImport || IsMdAbstract(pMethod->m_Attr) || IsMdPinvokeImpl(pMethod->m_Attr)
356 || IsMiRuntime(pMethod->m_wImplAttr) || IsMiInternalCall(pMethod->m_wImplAttr)) return TRUE;
359 report->error("Method has no body\n");
364 report->warn("Method has no body, 'ret' emitted\n");
365 Instr* pIns = GetInstr();
368 memset(pIns,0,sizeof(Instr));
369 pIns->opcode = CEE_RET;
375 if(pMethod->m_Locals.COUNT()) pMethod->m_LocalsSig=0x11000001; // placeholder, the real token 2b defined in EmitMethod
377 COR_ILMETHOD_FAT fatHeader;
378 fatHeader.SetFlags(pMethod->m_Flags);
379 fatHeader.SetMaxStack(pMethod->m_MaxStack);
380 fatHeader.SetLocalVarSigTok(pMethod->m_LocalsSig);
381 fatHeader.SetCodeSize(m_CurPC);
382 bool moreSections = (pMethod->m_dwNumExceptions != 0);
384 // if max stack is specified <8, force fat header, otherwise (with tiny header) it will default to 8
385 if((fatHeader.GetMaxStack() < 8)&&(fatHeader.GetLocalVarSigTok()==0)&&(fatHeader.GetCodeSize()<64)&&(!moreSections))
386 fatHeader.SetFlags(fatHeader.GetFlags() | CorILMethod_InitLocals); //forces fat header but does nothing else, since LocalVarSigTok==0
388 unsigned codeSize = m_CurPC;
389 unsigned codeSizeAligned = codeSize;
391 codeSizeAligned = (codeSizeAligned + 3) & ~3; // to insure EH section aligned
393 unsigned headerSize = COR_ILMETHOD::Size(&fatHeader, moreSections);
394 unsigned ehSize = COR_ILMETHOD_SECT_EH::Size(pMethod->m_dwNumExceptions, pMethod->m_ExceptionList);
395 unsigned totalSize = headerSize + codeSizeAligned + ehSize;
400 if((pbsBody = new BinStr())==NULL) return FALSE;
401 if((outBuff = pbsBody->getBuff(totalSize))==NULL) return FALSE;
402 endbuf = &outBuff[totalSize];
405 outBuff += COR_ILMETHOD::Emit(headerSize, &fatHeader, moreSections, outBuff);
407 pMethod->m_pCode = outBuff;
408 pMethod->m_headerOffset= PEFileOffset;
409 pMethod->m_methodOffset= PEFileOffset + headerSize;
410 pMethod->m_CodeSize = codeSize;
415 memset(outBuff,0,codeSizeAligned);
416 memcpy(outBuff, m_pOutputBuffer, codeSize);
417 outBuff += codeSizeAligned;
420 if(pMethod->m_dwNumExceptions)
423 COR_ILMETHOD_SECT_EH_CLAUSE_FAT* pEx;
424 DWORD TryEnd,HandlerEnd, dwEx, dwEf;
425 for(dwEx = 0, pEx = pMethod->m_ExceptionList; dwEx < pMethod->m_dwNumExceptions; dwEx++, pEx++)
427 if(pEx->GetTryOffset() > m_CurPC) // i.e., pMethod->m_CodeSize
429 report->error("Invalid SEH clause #%d: Try block starts beyond code size\n",dwEx+1);
431 TryEnd = pEx->GetTryOffset()+pEx->GetTryLength();
434 report->error("Invalid SEH clause #%d: Try block ends beyond code size\n",dwEx+1);
436 if(pEx->GetHandlerOffset() > m_CurPC)
438 report->error("Invalid SEH clause #%d: Handler block starts beyond code size\n",dwEx+1);
440 HandlerEnd = pEx->GetHandlerOffset()+pEx->GetHandlerLength();
441 if(HandlerEnd > m_CurPC)
443 report->error("Invalid SEH clause #%d: Handler block ends beyond code size\n",dwEx+1);
445 if(pEx->Flags & COR_ILEXCEPTION_CLAUSE_FILTER)
447 if(!((pEx->GetFilterOffset() >= TryEnd)||(pEx->GetTryOffset() >= HandlerEnd)))
449 report->error("Invalid SEH clause #%d: Try and Filter/Handler blocks overlap\n",dwEx+1);
451 for(dwEf = 0; dwEf < pMethod->m_dwNumEndfilters; dwEf++)
453 if(pMethod->m_EndfilterOffsetList[dwEf] == pEx->GetHandlerOffset()) break;
455 if(dwEf >= pMethod->m_dwNumEndfilters)
457 report->error("Invalid SEH clause #%d: Filter block separated from Handler, or not ending with endfilter\n",dwEx+1);
461 if(!((pEx->GetHandlerOffset() >= TryEnd)||(pEx->GetTryOffset() >= HandlerEnd)))
463 report->error("Invalid SEH clause #%d: Try and Handler blocks overlap\n",dwEx+1);
468 outBuff += COR_ILMETHOD_SECT_EH::Emit(ehSize, pMethod->m_dwNumExceptions,
469 pMethod->m_ExceptionList, false, outBuff);
471 _ASSERTE(outBuff == endbuf);
473 pMethod->m_pbsBody = pbsBody;
475 LocalMemberRefFixup* pMRF;
476 while((pMRF = pMethod->m_LocalMemberRefFixupList.POP()))
478 pMRF->offset += (size_t)(pMethod->m_pCode);
479 m_LocalMemberRefFixupList.PUSH(pMRF); // transfer MRF to assembler's list
482 if(m_fReportProgress)
484 if (pMethod->IsGlobalMethod())
485 report->msg("Assembled global method %s\n", pMethod->m_szName);
486 else report->msg("Assembled method %s::%s\n", pMethod->m_pClass->m_szFQN,
493 BOOL Assembler::EmitMethodBody(Method* pMethod, BinStr* pbsOut)
497 BinStr* pbsBody = pMethod->m_pbsBody;
499 if(pbsBody && (totalSize = pbsBody->length()))
501 unsigned headerSize = pMethod->m_methodOffset-pMethod->m_headerOffset;
502 MethodBody* pMB = NULL;
503 // ----------emit locals signature-------------------
505 if((uLocals = pMethod->m_Locals.COUNT()))
508 BinStr* pbsSig = new BinStr();
512 const COR_SIGNATURE* mySig;
514 pbsSig->appendInt8(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG);
515 cnt = CorSigCompressData(uLocals,pbsSig->getBuff(5));
516 pbsSig->remove(5-cnt);
517 for(cnt = 0; (pVD = pMethod->m_Locals.PEEK(cnt)); cnt++)
519 if(pVD->pbsSig) pbsSig->append(pVD->pbsSig);
522 report->error("Undefined type of local var slot %d in method %s\n",cnt,pMethod->m_szName);
523 pbsSig->appendInt8(ELEMENT_TYPE_I4);
527 cSig = pbsSig->length();
528 mySig = (const COR_SIGNATURE *)(pbsSig->ptr());
530 if (cSig > 1) // non-empty signature
532 hr = m_pEmitter->GetTokenFromSig(mySig, cSig, &pMethod->m_LocalsSig);
533 _ASSERTE(SUCCEEDED(hr));
536 COR_ILMETHOD_FAT* pFH; // Fat header guaranteed, because there are local vars
537 pFH = (COR_ILMETHOD_FAT*)(pMethod->m_pbsBody->ptr());
538 pFH->SetLocalVarSigTok(pMethod->m_LocalsSig);
540 //--------------------------------------------------------------------------------
541 if(m_fGeneratePDB && (m_pSymWriter != NULL))
543 m_pSymWriter->OpenMethod(pMethod->m_Tok);
544 ULONG N = pMethod->m_LinePCList.COUNT();
545 if(pMethod->m_fEntryPoint) m_pSymWriter->SetUserEntryPoint(pMethod->m_Tok);
549 ULONG32 *offsets=new ULONG32[N], *lines = new ULONG32[N], *columns = new ULONG32[N];
550 ULONG32 *endlines=new ULONG32[N], *endcolumns=new ULONG32[N];
551 if(offsets && lines && columns && endlines && endcolumns)
555 while((pDW = m_DocWriterList.PEEK(j++)))
557 if((m_pSymDocument = pDW->pWriter))
560 for(i=0, n=0; (pLPC = pMethod->m_LinePCList.PEEK(i)); i++)
562 if(pLPC->pWriter == m_pSymDocument)
564 offsets[n] = pLPC->PC;
565 lines[n] = pLPC->Line;
566 columns[n] = pLPC->Column;
567 endlines[n] = pLPC->LineEnd;
568 endcolumns[n] = pLPC->ColumnEnd;
572 if(n) m_pSymWriter->DefineSequencePoints(m_pSymDocument,n,
573 offsets,lines,columns,endlines,endcolumns);
574 } // end if(pSymDocument)
575 } // end while(pDW = next doc.writer)
576 pMethod->m_LinePCList.RESET(true);
578 else report->error("\nOutOfMemory!\n");
583 delete [] endcolumns;
586 if(pMethod->m_ulLines[1])
587 hrr = m_pSymWriter->SetMethodSourceRange(m_pSymDocument,pMethod->m_ulLines[0], pMethod->m_ulColumns[0],
588 m_pSymDocument,pMethod->m_ulLines[1], pMethod->m_ulColumns[1]);
589 EmitScope(&(pMethod->m_MainScope)); // recursively emits all nested scopes
591 m_pSymWriter->CloseMethod();
592 } // end if(fIncludeDebugInfo)
593 //-----------------------------------------------------
597 for(int k=0; (pMB = m_MethodBodyList.PEEK(k)) != NULL; k++)
599 if((pMB->pbsBody->length() == totalSize)
600 && (memcmp(pMB->pbsBody->ptr(), pbsBody->ptr(),totalSize)==0))
605 pMethod->m_headerOffset= pMB->RVA;
606 pMethod->m_methodOffset= pMB->RVA + headerSize;
607 pMethod->m_pCode = pMB->pCode;
609 pMethod->m_pbsBody = NULL;
616 unsigned align = (headerSize == 1)? 1 : 4;
617 ULONG PEFileOffset, methodRVA;
622 PEFileOffset = pbsOut->length();
624 while(PEFileOffset & align)
626 pbsOut->appendInt8(0);
629 pbsOut->append(pbsBody);
630 outBuff = (BYTE*)(pbsOut->ptr()) + (pbsOut->length() - pbsBody->length());
637 if (FAILED(m_pCeeFileGen->GetSectionBlock (m_pILSection, totalSize,
638 align, (void **) &outBuff))) return FALSE;
639 memcpy(outBuff,pbsBody->ptr(),totalSize);
640 // The offset where we start, (not where the alignment bytes start!
641 if (FAILED(m_pCeeFileGen->GetSectionDataLen (m_pILSection, &PEFileOffset)))
643 PEFileOffset -= totalSize;
646 pMethod->m_pCode = outBuff + headerSize;
647 pMethod->m_headerOffset= PEFileOffset;
648 pMethod->m_methodOffset= PEFileOffset + headerSize;
649 DoDeferredILFixups(pMethod);
651 if(m_fENCMode) methodRVA = PEFileOffset;
652 else m_pCeeFileGen->GetMethodRVA(m_pCeeFile, PEFileOffset,&methodRVA);
654 pMethod->m_headerOffset= methodRVA;
655 pMethod->m_methodOffset= methodRVA + headerSize;
658 if((pMB = new MethodBody)==NULL) return FALSE;
659 pMB->pbsBody = pbsBody;
660 pMB->RVA = methodRVA;
661 pMB->pCode = pMethod->m_pCode;
662 m_MethodBodyList.PUSH(pMB);
666 //pMethod->m_pbsBody = NULL;
668 m_pEmitter->SetRVA(pMethod->m_Tok,pMethod->m_headerOffset);
675 ImportDescriptor* Assembler::EmitImport(BinStr* DllName)
678 ImportDescriptor* pID;
681 if(DllName) l = DllName->length(); // No zero terminator here!
684 sz = (char*)DllName->ptr();
685 while((pID=m_ImportList.PEEK(i++)))
687 if((pID->dwDllName== (DWORD) l)&& !memcmp(pID->szDllName,sz,l)) return pID;
692 while((pID=m_ImportList.PEEK(i++)))
694 if(pID->dwDllName==0) return pID;
697 if((pID = new ImportDescriptor(sz,l)))
699 m_ImportList.PUSH(pID);
700 pID->mrDll = TokenFromRid(m_ImportList.COUNT(),mdtModuleRef);
703 else report->error("Failed to allocate import descriptor\n");
707 void Assembler::EmitImports()
709 WCHAR* wzDllName=&wzUniBuf[0];
710 ImportDescriptor* pID;
713 for(i=0; (pID = m_ImportList.PEEK(i)); i++)
715 WszMultiByteToWideChar(g_uCodePage,0,pID->szDllName,-1,wzDllName,dwUniBuf-1);
716 if(FAILED(m_pEmitter->DefineModuleRef( // S_OK or error.
717 wzDllName, // [IN] DLL name
718 &tk))) // [OUT] returned
719 report->error("Failed to define module ref '%s'\n",pID->szDllName);
721 _ASSERTE(tk == pID->mrDll);
725 HRESULT Assembler::EmitPinvokeMap(mdToken tk, PInvokeDescriptor* pDescr)
727 WCHAR* wzAlias=&wzUniBuf[0];
729 if(pDescr->szAlias) WszMultiByteToWideChar(g_uCodePage,0,pDescr->szAlias,-1,wzAlias,dwUniBuf-1);
731 return m_pEmitter->DefinePinvokeMap( // Return code.
732 tk, // [IN] FieldDef, MethodDef or MethodImpl.
733 pDescr->dwAttrs, // [IN] Flags used for mapping.
734 (LPCWSTR)wzAlias, // [IN] Import name.
735 pDescr->mrDll); // [IN] ModuleRef token for the target DLL.
738 void Assembler::EmitScope(Scope* pSCroot)
740 static ULONG32 scopeID;
741 static ARG_NAME_LIST *pVarList;
743 WCHAR* wzVarName=&wzUniBuf[0];
744 char* szPhonyName=(char*)&wzUniBuf[dwUniBuf >> 1];
745 Scope* pSC = pSCroot;
746 if(pSC && m_pSymWriter)
748 if(SUCCEEDED(m_pSymWriter->OpenScope(pSC->dwStart,&scopeID)))
752 for(pVarList = pSC->pLocals; pVarList; pVarList = pVarList->pNext)
756 if((pVarList->szName)&&(*(pVarList->szName))) strcpy_s(szPhonyName,dwUniBuf >> 1,pVarList->szName);
757 else sprintf_s(szPhonyName,(dwUniBuf >> 1),"V_%d",pVarList->dwAttr);
759 WszMultiByteToWideChar(g_uCodePage,0,szPhonyName,-1,wzVarName,dwUniBuf >> 1);
761 m_pSymWriter->DefineLocalVariable(wzVarName,0,pVarList->pSig->length(),
762 (BYTE*)pVarList->pSig->ptr(),ADDR_IL_OFFSET,pVarList->dwAttr,0,0,0,0);
766 report->error("Local Var '%s' has no signature\n",pVarList->szName);
770 for(i = 0; (pSC = pSCroot->SubScope.PEEK(i)); i++) EmitScope(pSC);
771 m_pSymWriter->CloseScope(pSCroot->dwEnd);
776 BOOL Assembler::EmitMethod(Method *pMethod)
778 // Emit the metadata for a method definition
779 BOOL fSuccess = FALSE;
780 WCHAR* wzMemberName=&wzUniBuf[0];
784 mdMethodDef MethodToken;
785 mdTypeDef ClassToken = mdTypeDefNil;
787 COR_SIGNATURE *mySig;
789 _ASSERTE((m_pCeeFileGen != NULL) && (pMethod != NULL));
790 fIsInterface = ((pMethod->m_pClass != NULL) && IsTdInterface(pMethod->m_pClass->m_Attr));
793 pszMethodName = pMethod->m_szName;
794 mySig = pMethod->m_pMethodSig;
795 cSig = pMethod->m_dwMethodCSig;
797 // If this is an instance method, make certain the signature says so
799 if (!(pMethod->m_Attr & mdStatic))
800 *mySig |= IMAGE_CEE_CS_CALLCONV_HASTHIS;
802 ClassToken = (pMethod->IsGlobalMethod())? mdTokenNil
803 : pMethod->m_pClass->m_cl;
804 // Convert name to UNICODE
805 WszMultiByteToWideChar(g_uCodePage,0,pszMethodName,-1,wzMemberName,dwUniBuf-1);
807 if(IsMdPrivateScope(pMethod->m_Attr))
809 WCHAR* p = wcsstr(wzMemberName,W("$PST06"));
813 if (FAILED(m_pEmitter->DefineMethod(ClassToken, // parent class
814 wzMemberName, // member name
815 pMethod->m_Attr & ~mdReservedMask, // member attributes
816 mySig, // member signature
819 pMethod->m_wImplAttr, // implflags
822 report->error("Failed to define method '%s'\n",pszMethodName);
825 pMethod->m_Tok = MethodToken;
826 //--------------------------------------------------------------------------------
827 // the only way to set mdRequireSecObject:
828 if(pMethod->m_Attr & mdRequireSecObject)
830 mdToken tkPseudoClass;
831 if(FAILED(m_pEmitter->DefineTypeRefByName(1, COR_REQUIRES_SECOBJ_ATTRIBUTE, &tkPseudoClass)))
832 report->error("Unable to define type reference '%s'\n", COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI);
835 mdToken tkPseudoCtor;
836 BYTE bSig[3] = {IMAGE_CEE_CS_CALLCONV_HASTHIS,0,ELEMENT_TYPE_VOID};
837 if(FAILED(m_pEmitter->DefineMemberRef(tkPseudoClass, W(".ctor"), (PCCOR_SIGNATURE)bSig, 3, &tkPseudoCtor)))
838 report->error("Unable to define member reference '%s::.ctor'\n", COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI);
839 else DefineCV(new CustomDescr(MethodToken,tkPseudoCtor,NULL));
843 if (pMethod->m_NumTyPars)
848 for(i = 0; i < pMethod->m_NumTyPars; i++)
850 //ptk = (pMethod->m_TyParBounds[i] == NULL)? NULL : (mdToken*)(pMethod->m_TyParBounds[i]->ptr());
851 //if(FAILED(m_pEmitter->DefineGenericParam(MethodToken,i,0,pMethod->m_TyParNames[i],0,ptk,&tk)))
852 ptk = (pMethod->m_TyPars[i].Bounds() == NULL)? NULL : (mdToken*)(pMethod->m_TyPars[i].Bounds()->ptr());
853 if(FAILED(m_pEmitter->DefineGenericParam(MethodToken,i,pMethod->m_TyPars[i].Attrs(),pMethod->m_TyPars[i].Name(),0,ptk,&tk)))
854 report->error("Unable to define generic param'\n");
856 EmitCustomAttributes(tk, pMethod->m_TyPars[i].CAList());
859 //--------------------------------------------------------------------------------
860 EmitSecurityInfo(MethodToken,
861 pMethod->m_pPermissions,
862 pMethod->m_pPermissionSets);
863 //--------------------------------------------------------------------------------
864 if (pMethod->m_fEntryPoint)
866 if(fIsInterface) report->error("Entrypoint in Interface: Method '%s'\n",pszMethodName);
868 if (FAILED(m_pCeeFileGen->SetEntryPoint(m_pCeeFile, MethodToken)))
870 report->error("Failed to set entry point for method '%s'\n",pszMethodName);
875 //--------------------------------------------------------------------------------
876 if(IsMdPinvokeImpl(pMethod->m_Attr))
878 if(pMethod->m_pPInvoke)
881 if(pMethod->m_pPInvoke->szAlias == NULL) pMethod->m_pPInvoke->szAlias = pszMethodName;
882 hr = EmitPinvokeMap(MethodToken,pMethod->m_pPInvoke);
883 if(pMethod->m_pPInvoke->szAlias == pszMethodName) pMethod->m_pPInvoke->szAlias = NULL;
887 report->error("Failed to set PInvoke map for method '%s'\n",pszMethodName);
893 { // add parameters to metadata
894 void const *pValue=NULL;
896 DWORD dwCPlusTypeFlag=0;
898 WCHAR* wzParName=&wzUniBuf[0];
899 char* szPhonyName=(char*)&wzUniBuf[dwUniBuf >> 1];
900 if(pMethod->m_dwRetAttr || pMethod->m_pRetMarshal || pMethod->m_RetCustDList.COUNT())
902 if(pMethod->m_pRetValue)
904 dwCPlusTypeFlag= (DWORD)*(pMethod->m_pRetValue->ptr());
905 pValue = (void const *)(pMethod->m_pRetValue->ptr()+1);
906 cbValue = pMethod->m_pRetValue->length()-1;
907 if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR);
915 m_pEmitter->DefineParam(MethodToken,0,NULL,pMethod->m_dwRetAttr,dwCPlusTypeFlag,pValue,cbValue,&pdef);
917 if(pMethod->m_pRetMarshal)
919 if(FAILED(m_pEmitter->SetFieldMarshal (
920 pdef, // [IN] given a fieldDef or paramDef token
921 (PCCOR_SIGNATURE)(pMethod->m_pRetMarshal->ptr()), // [IN] native type specification
922 pMethod->m_pRetMarshal->length()))) // [IN] count of bytes of pvNativeType
923 report->error("Failed to set param marshaling for return\n");
926 EmitCustomAttributes(pdef, &(pMethod->m_RetCustDList));
928 for(ARG_NAME_LIST *pAN=pMethod->m_firstArgName; pAN; pAN = pAN->pNext)
930 if(pAN->nNum >= 65535)
932 report->error("Method '%s': Param.sequence number (%d) exceeds 65535, unable to define parameter\n",pszMethodName,pAN->nNum+1);
935 if(pAN->dwName) strcpy_s(szPhonyName,dwUniBuf >> 1,pAN->szName);
936 else sprintf_s(szPhonyName,(dwUniBuf >> 1),"A_%d",pAN->nNum);
938 WszMultiByteToWideChar(g_uCodePage,0,szPhonyName,-1,wzParName,dwUniBuf >> 1);
942 dwCPlusTypeFlag= (DWORD)*(pAN->pValue->ptr());
943 pValue = (void const *)(pAN->pValue->ptr()+1);
944 cbValue = pAN->pValue->length()-1;
945 if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR);
953 m_pEmitter->DefineParam(MethodToken,pAN->nNum+1,wzParName,pAN->dwAttr,dwCPlusTypeFlag,pValue,cbValue,&pdef);
956 if(FAILED(m_pEmitter->SetFieldMarshal (
957 pdef, // [IN] given a fieldDef or paramDef token
958 (PCCOR_SIGNATURE)(pAN->pMarshal->ptr()), // [IN] native type specification
959 pAN->pMarshal->length()))) // [IN] count of bytes of pvNativeType
960 report->error("Failed to set param marshaling for '%s'\n",pAN->szName);
962 EmitCustomAttributes(pdef, &(pAN->CustDList));
966 //--------------------------------------------------------------------------------
967 // Update method implementations for this method
969 MethodImplDescriptor* pMID;
971 for(i=0;(pMID = pMethod->m_MethodImplDList.PEEK(i));i++)
973 pMID->m_tkImplementingMethod = MethodToken;
974 // don't delete it here, it's still in the general list
977 //--------------------------------------------------------------------------------
978 EmitCustomAttributes(MethodToken, &(pMethod->m_CustomDescrList));
980 if (fSuccess == FALSE) m_State = STATE_FAIL;
984 BOOL Assembler::EmitMethodImpls()
986 MethodImplDescriptor* pMID;
989 for(i=0; (pMID = m_MethodImplDList.PEEK(i)); i++)
991 if(m_fENCMode && (!pMID->m_fNew)) continue;
992 pMID->m_tkImplementingMethod = ResolveLocalMemberRef(pMID->m_tkImplementingMethod);
993 pMID->m_tkImplementedMethod = ResolveLocalMemberRef(pMID->m_tkImplementedMethod);
994 if(FAILED(m_pEmitter->DefineMethodImpl( pMID->m_tkDefiningClass,
995 pMID->m_tkImplementingMethod,
996 pMID->m_tkImplementedMethod)))
998 report->error("Failed to define Method Implementation");
1001 pMID->m_fNew = FALSE;
1006 mdToken Assembler::ResolveLocalMemberRef(mdToken tok)
1008 if(TypeFromToken(tok) == 0x99000000)
1010 tok = RidFromToken(tok);
1011 if(tok) tok = m_LocalMethodRefDList.PEEK(tok-1)->m_tkResolved;
1013 else if(TypeFromToken(tok) == 0x98000000)
1015 tok = RidFromToken(tok);
1016 if(tok) tok = m_LocalFieldRefDList.PEEK(tok-1)->m_tkResolved;
1021 BOOL Assembler::EmitEvent(EventDescriptor* pED)
1023 mdMethodDef mdAddOn=mdMethodDefNil,
1024 mdRemoveOn=mdMethodDefNil,
1025 mdFire=mdMethodDefNil,
1028 WCHAR* wzMemberName=&wzUniBuf[0];
1030 if(!pED) return FALSE;
1032 WszMultiByteToWideChar(g_uCodePage,0,pED->m_szName,-1,wzMemberName,dwUniBuf-1);
1034 mdAddOn = ResolveLocalMemberRef(pED->m_tkAddOn);
1035 if(TypeFromToken(mdAddOn) != mdtMethodDef)
1037 report->error("Invalid Add method of event '%s'\n",pED->m_szName);
1040 mdRemoveOn = ResolveLocalMemberRef(pED->m_tkRemoveOn);
1041 if(TypeFromToken(mdRemoveOn) != mdtMethodDef)
1043 report->error("Invalid Remove method of event '%s'\n",pED->m_szName);
1046 mdFire = ResolveLocalMemberRef(pED->m_tkFire);
1047 if((RidFromToken(mdFire)!=0)&&(TypeFromToken(mdFire) != mdtMethodDef))
1049 report->error("Invalid Fire method of event '%s'\n",pED->m_szName);
1053 nOthers = pED->m_tklOthers.COUNT();
1054 mdOthers = new mdMethodDef[nOthers+1];
1055 if(mdOthers == NULL)
1057 report->error("Failed to allocate Others array for event descriptor\n");
1060 for(int j=0; j < nOthers; j++)
1062 mdOthers[j] = ResolveLocalMemberRef((mdToken)(UINT_PTR)(pED->m_tklOthers.PEEK(j))); // @WARNING: casting down from 'mdToken*' to 'mdToken'
1064 mdOthers[nOthers] = mdMethodDefNil; // like null-terminator
1066 if(FAILED(m_pEmitter->DefineEvent( pED->m_tdClass,
1074 &(pED->m_edEventTok))))
1076 report->error("Failed to define event '%s'.\n",pED->m_szName);
1080 EmitCustomAttributes(pED->m_edEventTok, &(pED->m_CustomDescrList));
1084 BOOL Assembler::EmitProp(PropDescriptor* pPD)
1086 mdMethodDef mdSet, mdGet, *mdOthers;
1088 WCHAR* wzMemberName=&wzUniBuf[0];
1090 if(!pPD) return FALSE;
1092 WszMultiByteToWideChar(g_uCodePage,0,pPD->m_szName,-1,wzMemberName,dwUniBuf-1);
1094 mdSet = ResolveLocalMemberRef(pPD->m_tkSet);
1095 if((RidFromToken(mdSet)!=0)&&(TypeFromToken(mdSet) != mdtMethodDef))
1097 report->error("Invalid Set method of property '%s'\n",pPD->m_szName);
1100 mdGet = ResolveLocalMemberRef(pPD->m_tkGet);
1101 if((RidFromToken(mdGet)!=0)&&(TypeFromToken(mdGet) != mdtMethodDef))
1103 report->error("Invalid Get method of property '%s'\n",pPD->m_szName);
1107 nOthers = pPD->m_tklOthers.COUNT();
1108 mdOthers = new mdMethodDef[nOthers+1];
1109 if(mdOthers == NULL)
1111 report->error("Failed to allocate Others array for prop descriptor\n");
1114 for(int j=0; j < nOthers; j++)
1116 mdOthers[j] = ResolveLocalMemberRef((mdToken)(UINT_PTR)(pPD->m_tklOthers.PEEK(j))); // @WARNING: casting down from 'mdToken*' to 'mdToken'
1118 if((RidFromToken(mdOthers[j])!=0)&&(TypeFromToken(mdOthers[j]) != mdtMethodDef))
1120 report->error("Invalid Other method of property '%s'\n",pPD->m_szName);
1126 mdOthers[nOthers] = mdMethodDefNil; // like null-terminator
1128 if(FAILED(m_pEmitter->DefineProperty( pPD->m_tdClass,
1133 pPD->m_dwCPlusTypeFlag,
1139 &(pPD->m_pdPropTok))))
1141 report->error("Failed to define property '%s'.\n",pPD->m_szName);
1145 EmitCustomAttributes(pPD->m_pdPropTok, &(pPD->m_CustomDescrList));
1149 Class *Assembler::FindCreateClass(__in __nullterminated const char *pszFQN)
1151 Class *pSearch = NULL;
1155 dummyClass->m_szFQN = pszFQN;
1156 dummyClass->m_Hash = hash((BYTE*)pszFQN, (unsigned)strlen(pszFQN), 10);
1157 pSearch = m_hshClass.FIND(dummyClass);
1158 dummyClass->m_szFQN = NULL;
1159 dummyClass->m_Hash = 0;
1164 DWORD dwFQN = (DWORD)strlen(pszFQN);
1166 Class *pEncloser = NULL;
1167 char* pszNewFQN = new char[dwFQN+1];
1168 strcpy_s(pszNewFQN,dwFQN+1,pszFQN);
1169 if((pch = strrchr(pszNewFQN, NESTING_SEP)) != NULL)
1172 pEncloser = FindCreateClass(pszNewFQN);
1175 pSearch = new Class(pszNewFQN);
1176 if (pSearch == NULL)
1177 report->error("Failed to create class '%s'\n",pszNewFQN);
1180 pSearch->m_pEncloser = pEncloser;
1181 m_lstClass.PUSH(pSearch);
1182 pSearch->m_cl = mdtTypeDef | m_lstClass.COUNT();
1183 m_hshClass.PUSH(pSearch);
1192 BOOL Assembler::EmitClass(Class *pClass)
1195 WCHAR* wzFullName=&wzUniBuf[0];
1196 HRESULT hr = E_FAIL;
1201 if(pClass == NULL) return FALSE;
1203 hr = CoCreateGuid(&guid);
1206 printf("Unable to create GUID\n");
1207 m_State = STATE_FAIL;
1211 if(pClass->m_pEncloser)
1212 szFullName = strrchr(pClass->m_szFQN,NESTING_SEP) + 1;
1214 szFullName = pClass->m_szFQN;
1216 WszMultiByteToWideChar(g_uCodePage,0,szFullName,-1,wzFullName,dwUniBuf);
1218 L = wcslen(wzFullName);
1219 if((L==0)||(wzFullName[L-1]==L'.')) // Missing class name!
1221 wcscat_s(wzFullName,dwUniBuf,W("$UNNAMED_TYPE$"));
1224 pClass->m_Attr = CheckClassFlagsIfNested(pClass->m_pEncloser, pClass->m_Attr);
1226 if (pClass->m_pEncloser)
1228 hr = m_pEmitter->DefineNestedType( wzFullName,
1229 pClass->m_Attr, // attributes
1230 pClass->m_crExtends, // CR extends class
1231 pClass->m_crImplements,// implements
1232 pClass->m_pEncloser->m_cl, // Enclosing class.
1237 hr = m_pEmitter->DefineTypeDef( wzFullName,
1238 pClass->m_Attr, // attributes
1239 pClass->m_crExtends, // CR extends class
1240 pClass->m_crImplements,// implements
1243 _ASSERTE(tok == pClass->m_cl);
1244 if (FAILED(hr)) goto exit;
1245 if (pClass->m_NumTyPars)
1250 for(i = 0; i < pClass->m_NumTyPars; i++)
1252 //ptk = (pClass->m_TyParBounds[i] == NULL)? NULL : (mdToken*)(pClass->m_TyParBounds[i]->ptr());
1253 //if(FAILED(m_pEmitter->DefineGenericParam(pClass->m_cl,i,pClass->m_TyParAttrs[i],pClass->m_TyParNames[i],0,ptk,&tk)))
1254 ptk = (pClass->m_TyPars[i].Bounds() == NULL)? NULL : (mdToken*)(pClass->m_TyPars[i].Bounds()->ptr());
1255 if(FAILED(m_pEmitter->DefineGenericParam(pClass->m_cl,i,pClass->m_TyPars[i].Attrs(),pClass->m_TyPars[i].Name(),0,ptk,&tk)))
1256 report->error("Unable to define generic param'\n");
1258 EmitCustomAttributes(tk, pClass->m_TyPars[i].CAList());
1263 EmitCustomAttributes(pClass->m_cl, &(pClass->m_CustDList));
1267 return SUCCEEDED(hr);
1270 BOOL Assembler::DoGlobalFixups()
1272 GlobalFixup *pSearch;
1274 for (int i=0; (pSearch = m_lstGlobalFixup.PEEK(i)); i++)
1276 GlobalLabel * pLabel = FindGlobalLabel(pSearch->m_szLabel);
1279 report->error("Unable to find forward reference global label '%s'\n",
1280 pSearch->m_szLabel);
1282 m_State = STATE_FAIL;
1285 //BYTE * pReference = pSearch->m_pReference;
1286 //DWORD GlobalOffset = pLabel->m_GlobalOffset;
1287 //memcpy(pReference,&GlobalOffset,4);
1288 SET_UNALIGNED_VAL32(pSearch->m_pReference,pLabel->m_GlobalOffset);
1294 state_t Assembler::AddGlobalLabel(__in __nullterminated char *pszName, HCEESECTION section)
1296 if (FindGlobalLabel(pszName) != NULL)
1298 report->error("Duplicate global label '%s'\n", pszName);
1299 m_State = STATE_FAIL;
1306 hr = m_pCeeFileGen->GetSectionDataLen(section, &GlobalOffset);
1307 _ASSERTE(SUCCEEDED(hr));
1309 GlobalLabel *pNew = new GlobalLabel(pszName, GlobalOffset, section);
1312 report->error("Failed to allocate global label '%s'\n",pszName);
1313 m_State = STATE_FAIL;
1317 m_lstGlobalLabel.PUSH(pNew);
1321 void Assembler::AddLabel(DWORD CurPC, __in __nullterminated char *pszName)
1323 if (m_pCurMethod->FindLabel(pszName) != NULL)
1325 report->error("Duplicate label: '%s'\n", pszName);
1327 m_State = STATE_FAIL;
1331 Label *pNew = new Label(pszName, CurPC);
1334 //m_pCurMethod->m_lstLabel.PUSH(pNew);
1335 m_lstLabel.PUSH(pNew);
1338 report->error("Failed to allocate label '%s'\n",pszName);
1339 m_State = STATE_FAIL;
1344 void Assembler::DoDeferredILFixups(Method* pMethod)
1345 { // Now that we know where in the file the code bytes will wind up,
1346 // we can update the RVAs and offsets.
1349 GlobalFixup *Fix = NULL;
1351 for (i=0;(pSearch = pMethod->m_lstILFixup.PEEK(i));i++)
1353 switch(pSearch->m_Kind)
1356 Fix = pSearch->m_Fixup;
1357 _ASSERTE(Fix != NULL);
1358 Fix->m_pReference = pMethod->m_pCode+pSearch->m_OffsetInMethod;
1362 hr = m_pCeeFileGen->AddSectionReloc(m_pILSection,
1363 pSearch->m_OffsetInMethod+pMethod->m_methodOffset,
1366 _ASSERTE(SUCCEEDED(hr));
1370 hr = m_pCeeFileGen->AddSectionReloc(m_pILSection,
1371 pSearch->m_OffsetInMethod+pMethod->m_methodOffset,
1372 m_pGlobalDataSection,
1374 _ASSERTE(SUCCEEDED(hr));
1382 /**************************************************************************/
1383 BOOL Assembler::DoFixups(Method* pMethod)
1387 for (int i=0; (pSearch = pMethod->m_lstFixup.PEEK(i)); i++)
1389 Label * pLabel = pMethod->FindLabel(pSearch->m_szLabel);
1394 report->error("Unable to find forward reference label '%s' called from PC=%d\n",
1395 pSearch->m_szLabel, pSearch->m_RelativeToPC);
1397 //m_State = STATE_FAIL;
1401 offset = pLabel->m_PC - pSearch->m_RelativeToPC;
1403 if (pSearch->m_FixupSize == 1)
1405 if (offset > 127 || offset < -128)
1407 report->error("Offset of forward reference label '%s' called from PC=%d is too large for 1 byte pcrel\n",
1408 pLabel->m_szName, pSearch->m_RelativeToPC);
1410 //m_State = STATE_FAIL;
1414 *pSearch->m_pBytes = (BYTE) offset;
1416 else if (pSearch->m_FixupSize == 4)
1418 SET_UNALIGNED_VAL32(pSearch->m_pBytes,offset);
1426 OPCODE Assembler::DecodeOpcode(const BYTE *pCode, DWORD *pdwLen)
1431 opcode = OPCODE(pCode[0]);
1434 opcode = OPCODE(pCode[1] + 256);
1435 if (opcode < 0 || opcode >= CEE_COUNT)
1454 char* Assembler::ReflectionNotation(mdToken tk)
1456 char *sz = (char*)&wzUniBuf[dwUniBuf>>1], *pc;
1458 switch(TypeFromToken(tk))
1462 Class *pClass = m_lstClass.PEEK(RidFromToken(tk)-1);
1465 strcpy_s(sz,dwUniBuf>>1,pClass->m_szFQN);
1467 while((pc = strchr(pc,NESTING_SEP)) != NULL)
1480 if(SUCCEEDED(m_pImporter->GetTypeRefProps(tk,&tkResScope,wzUniBuf,dwUniBuf>>1,&N)))
1482 WszWideCharToMultiByte(CP_UTF8,0,wzUniBuf,-1,sz,dwUniBuf>>1,NULL,NULL);
1483 if(TypeFromToken(tkResScope)==mdtAssemblyRef)
1485 AsmManAssembly *pAsmRef = m_pManifest->m_AsmRefLst.PEEK(RidFromToken(tkResScope)-1);
1488 pc = &sz[strlen(sz)];
1489 pc+=sprintf_s(pc,(dwUniBuf >> 1),", %s, Version=%d.%d.%d.%d, Culture=",pAsmRef->szName,
1490 pAsmRef->usVerMajor,pAsmRef->usVerMinor,pAsmRef->usBuild,pAsmRef->usRevision);
1492 if(pAsmRef->pLocale && (L=pAsmRef->pLocale->length()))
1494 memcpy(wzUniBuf,pAsmRef->pLocale->ptr(),L);
1496 WszWideCharToMultiByte(CP_UTF8,0,wzUniBuf,-1,pc,dwUniBuf>>1,NULL,NULL);
1498 else pc+=sprintf_s(pc,(dwUniBuf >> 1),"neutral");
1499 pc = &sz[strlen(sz)];
1500 if(pAsmRef->pPublicKeyToken && (L=pAsmRef->pPublicKeyToken->length()))
1502 pc+=sprintf_s(pc,(dwUniBuf >> 1),", Publickeytoken=");
1503 BYTE* pb = (BYTE*)(pAsmRef->pPublicKeyToken->ptr());
1504 for(N=0; N<L; N++,pb++) pc+=sprintf_s(pc,(dwUniBuf >> 1),"%2.2x",*pb);
1519 --------------------------------------------------------------------
1520 mix -- mix 3 32-bit values reversibly.
1521 For every delta with one or two bits set, and the deltas of all three
1522 high bits or all three low bits, whether the original value of a,b,c
1523 is almost all zero or is uniformly distributed,
1524 * If mix() is run forward or backward, at least 32 bits in a,b,c
1525 have at least 1/4 probability of changing.
1526 * If mix() is run forward, every bit of c will change between 1/3 and
1527 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
1528 mix() was built out of 36 single-cycle latency instructions in a
1529 structure that could supported 2x parallelism, like so:
1531 a -= c; x = (c>>13);
1535 c -= b; x = (b>>13);
1537 Unfortunately, superscalar Pentiums and Sparcs can't take advantage
1538 of that parallelism. They've also turned some of those single-cycle
1539 latency instructions into multi-cycle latency instructions. Still,
1540 this is the fastest good hash I could find. There were about 2^^68
1541 to choose from. I only looked at a billion or so.
1542 --------------------------------------------------------------------
1544 #define mix(a,b,c) \
1546 a -= b; a -= c; a ^= (c >> 13); \
1547 b -= c; b -= a; b ^= (a << 8); \
1548 c -= a; c -= b; c ^= (b >> 13); \
1549 a -= b; a -= c; a ^= (c >> 12); \
1550 b -= c; b -= a; b ^= (a << 16); \
1551 c -= a; c -= b; c ^= (b >> 5); \
1552 a -= b; a -= c; a ^= (c >> 3); \
1553 b -= c; b -= a; b ^= (a << 10); \
1554 c -= a; c -= b; c ^= (b >> 15); \
1558 --------------------------------------------------------------------
1559 hash() -- hash a variable-length key into a 32-bit value
1560 k : the key (the unaligned variable-length array of bytes)
1561 len : the length of the key, counting by bytes
1562 initval : can be any 4-byte value
1563 Returns a 32-bit value. Every bit of the key affects every bit of
1564 the return value. Every 1-bit and 2-bit delta achieves avalanche.
1565 About 6*len+35 instructions.
1567 The best hash table sizes are powers of 2. There is no need to do
1568 mod a prime (mod is sooo slow!). If you need less than 32 bits,
1569 use a bitmask. For example, if you need only 10 bits, do
1570 h = (h & hashmask(10));
1571 In which case, the hash table should have hashsize(10) elements.
1573 If you are hashing n strings (ub1 **)k, do it like this:
1574 for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
1576 By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
1577 code any way you wish, private, educational, or commercial. It's free.
1579 See http://burtleburtle.net/bob/hash/evahash.html
1580 Use for hash table lookup, or anything where one collision in 2^^32 is
1581 acceptable. Do NOT use for cryptographic purposes.
1582 --------------------------------------------------------------------
1586 __in_ecount(length) const BYTE *k, /* the key */
1587 unsigned length, /* the length of the key */
1588 unsigned initval) /* the previous hash, or an arbitrary value */
1590 register unsigned a,b,c,len;
1592 /* Set up the internal state */
1594 a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
1595 c = initval; /* the previous hash value */
1597 /*---------------------------------------- handle most of the key */
1600 a += (k[0] + ((unsigned)k[1] << 8) + ((unsigned)k[2] << 16) + ((unsigned)k[3] << 24));
1601 b += (k[4] + ((unsigned)k[5] << 8) + ((unsigned)k[6] << 16) + ((unsigned)k[7] << 24));
1602 c += (k[8] + ((unsigned)k[9] << 8) + ((unsigned)k[10] << 16) + ((unsigned)k[11] << 24));
1607 /*------------------------------------- handle the last 11 bytes */
1609 switch(len) /* all the case statements fall through */
1611 case 11: c+=((unsigned)k[10] << 24);
1612 case 10: c+=((unsigned)k[9] << 16);
1613 case 9 : c+=((unsigned)k[8] << 8);
1614 /* the first byte of c is reserved for the length */
1615 case 8 : b+=((unsigned)k[7] << 24);
1616 case 7 : b+=((unsigned)k[6] << 16);
1617 case 6 : b+=((unsigned)k[5] << 8);
1619 case 4 : a+=((unsigned)k[3] << 24);
1620 case 3 : a+=((unsigned)k[2] << 16);
1621 case 2 : a+=((unsigned)k[1] << 8);
1623 /* case 0: nothing left to add */
1626 /*-------------------------------------------- report the result */