ClassFieldLayoutCalculator.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. #include "ClassFieldLayoutCalculator.h"
  2. #include "metadata/FieldLayout.h"
  3. #include "metadata/GenericMetadata.h"
  4. #include "vm/Field.h"
  5. #include "InterpreterImage.h"
  6. namespace hybridclr
  7. {
  8. namespace metadata
  9. {
  10. typedef void* voidptr_t;
  11. #define IL2CPP_ALIGN_STRUCT(type) struct type ## AlignStruct {uint8_t pad; type t; };
  12. IL2CPP_ALIGN_STRUCT(voidptr_t)
  13. IL2CPP_ALIGN_STRUCT(int8_t)
  14. IL2CPP_ALIGN_STRUCT(int16_t)
  15. IL2CPP_ALIGN_STRUCT(int32_t)
  16. IL2CPP_ALIGN_STRUCT(int64_t)
  17. IL2CPP_ALIGN_STRUCT(intptr_t)
  18. IL2CPP_ALIGN_STRUCT(float)
  19. IL2CPP_ALIGN_STRUCT(double)
  20. #define IL2CPP_ALIGN_OF(type) ((int32_t)offsetof(type ## AlignStruct, t))
  21. SizeAndAlignment ClassFieldLayoutCalculator::GetTypeSizeAndAlignment(const Il2CppType* type)
  22. {
  23. SizeAndAlignment sa = { };
  24. if (type->byref)
  25. {
  26. sa.size = sa.nativeSize = sizeof(voidptr_t);
  27. sa.alignment = IL2CPP_ALIGN_OF(voidptr_t);
  28. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  29. sa.naturalAlignment = sa.alignment;
  30. #endif
  31. return sa;
  32. }
  33. switch (type->type)
  34. {
  35. case IL2CPP_TYPE_I1:
  36. case IL2CPP_TYPE_U1:
  37. case IL2CPP_TYPE_BOOLEAN:
  38. sa.size = sa.nativeSize = sizeof(int8_t);
  39. sa.alignment = IL2CPP_ALIGN_OF(int8_t);
  40. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  41. sa.naturalAlignment = sa.alignment;
  42. #endif
  43. return sa;
  44. case IL2CPP_TYPE_I2:
  45. case IL2CPP_TYPE_U2:
  46. case IL2CPP_TYPE_CHAR:
  47. sa.size = sa.nativeSize = sizeof(int16_t);
  48. sa.alignment = IL2CPP_ALIGN_OF(int16_t);
  49. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  50. sa.naturalAlignment = sa.alignment;
  51. #endif
  52. return sa;
  53. case IL2CPP_TYPE_I4:
  54. case IL2CPP_TYPE_U4:
  55. sa.size = sa.nativeSize = sizeof(int32_t);
  56. sa.alignment = IL2CPP_ALIGN_OF(int32_t);
  57. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  58. sa.naturalAlignment = sa.alignment;
  59. #endif
  60. return sa;
  61. case IL2CPP_TYPE_I8:
  62. case IL2CPP_TYPE_U8:
  63. sa.size = sa.nativeSize = sizeof(int64_t);
  64. sa.alignment = IL2CPP_ALIGN_OF(int64_t);
  65. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  66. sa.naturalAlignment = sa.alignment;
  67. #endif
  68. return sa;
  69. case IL2CPP_TYPE_I:
  70. case IL2CPP_TYPE_U:
  71. // TODO should we use pointer or int32_t here?
  72. sa.size = sa.nativeSize = sizeof(intptr_t);
  73. sa.alignment = IL2CPP_ALIGN_OF(intptr_t);
  74. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  75. sa.naturalAlignment = sa.alignment;
  76. #endif
  77. return sa;
  78. case IL2CPP_TYPE_R4:
  79. sa.size = sa.nativeSize = sizeof(float);
  80. sa.alignment = IL2CPP_ALIGN_OF(float);
  81. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  82. sa.naturalAlignment = sa.alignment;
  83. #endif
  84. return sa;
  85. case IL2CPP_TYPE_R8:
  86. sa.size = sa.nativeSize = sizeof(double);
  87. sa.alignment = IL2CPP_ALIGN_OF(double);
  88. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  89. sa.naturalAlignment = sa.alignment;
  90. #endif
  91. return sa;
  92. case IL2CPP_TYPE_PTR:
  93. case IL2CPP_TYPE_FNPTR:
  94. case IL2CPP_TYPE_STRING:
  95. case IL2CPP_TYPE_SZARRAY:
  96. case IL2CPP_TYPE_ARRAY:
  97. case IL2CPP_TYPE_CLASS:
  98. case IL2CPP_TYPE_OBJECT:
  99. sa.size = sa.nativeSize = sizeof(voidptr_t);
  100. sa.alignment = IL2CPP_ALIGN_OF(voidptr_t);
  101. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  102. sa.naturalAlignment = sa.alignment;
  103. #endif
  104. return sa;
  105. case IL2CPP_TYPE_VAR:
  106. case IL2CPP_TYPE_MVAR:
  107. sa.size = sa.nativeSize = 1;
  108. sa.alignment = 1;
  109. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  110. sa.naturalAlignment = sa.alignment;
  111. #endif
  112. return sa;
  113. case IL2CPP_TYPE_VALUETYPE:
  114. {
  115. CalcClassNotStaticFields(type);
  116. ClassLayoutInfo& classLayout = *_classMap[type];
  117. sa.size = classLayout.instanceSize - sizeof(Il2CppObject);
  118. sa.nativeSize = classLayout.nativeSize;
  119. sa.alignment = classLayout.alignment;
  120. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  121. sa.naturalAlignment = classLayout.naturalAlignment;
  122. #endif
  123. return sa;
  124. }
  125. case IL2CPP_TYPE_GENERICINST:
  126. {
  127. Il2CppGenericClass* gclass = type->data.generic_class;
  128. //Il2CppClass* container_class = GenericClass::GetTypeDefinition(gclass);
  129. const Il2CppTypeDefinition* typeDef = GetUnderlyingTypeDefinition(type);
  130. if (IsValueType(typeDef))
  131. {
  132. CalcClassNotStaticFields(type);
  133. ClassLayoutInfo& classLayout = *_classMap[type];
  134. sa.size = classLayout.instanceSize - sizeof(Il2CppObject);
  135. sa.nativeSize = classLayout.nativeSize;
  136. sa.alignment = classLayout.alignment;
  137. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  138. sa.naturalAlignment = classLayout.naturalAlignment;
  139. #endif
  140. }
  141. else
  142. {
  143. sa.size = sa.nativeSize = sizeof(voidptr_t);
  144. sa.alignment = IL2CPP_ALIGN_OF(voidptr_t);
  145. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  146. sa.naturalAlignment = sa.alignment;
  147. #endif
  148. }
  149. return sa;
  150. }
  151. default:
  152. IL2CPP_ASSERT(0);
  153. break;
  154. }
  155. return sa;
  156. }
  157. static int32_t AlignTo(int32_t size, int32_t alignment)
  158. {
  159. if (size & (alignment - 1))
  160. {
  161. size += alignment - 1;
  162. size &= ~(alignment - 1);
  163. }
  164. return size;
  165. }
  166. void ClassFieldLayoutCalculator::LayoutFields(int32_t actualParentSize, int32_t parentAlignment, uint8_t packing, std::vector<FieldLayout*>& fields, FieldLayoutData& data)
  167. {
  168. //data.classSize = parentSize;
  169. data.actualClassSize = actualParentSize;
  170. IL2CPP_ASSERT(parentAlignment <= std::numeric_limits<uint8_t>::max());
  171. data.minimumAlignment = static_cast<uint8_t>(parentAlignment);
  172. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  173. data.naturalAlignment = 0;
  174. #endif
  175. data.nativeSize = 0;
  176. for (FieldLayout* field : fields)
  177. {
  178. SizeAndAlignment sa = GetTypeSizeAndAlignment(field->type);
  179. field->size = sa.size;// sa.nativeSize > 0 ? sa.nativeSize : sa.size;
  180. // For fields, we might not want to take the actual alignment of the type - that might account for
  181. // packing. When a type is used as a field, we should not care about its alignment with packing,
  182. // instead let's use its natural alignment, without regard for packing. So if it's alignment
  183. // is less than the compiler's minimum alignment (4 bytes), lets use the natural alignment if we have it.
  184. uint8_t alignment = sa.alignment;
  185. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  186. if (alignment < 4 && sa.naturalAlignment != 0)
  187. alignment = sa.naturalAlignment;
  188. #endif
  189. if (packing != 0)
  190. alignment = std::min(sa.alignment, packing);
  191. int32_t offset = data.actualClassSize;
  192. offset += alignment - 1;
  193. offset &= ~(alignment - 1);
  194. field->offset = offset;
  195. data.FieldOffsets.push_back(offset);
  196. data.actualClassSize = offset + std::max(sa.size, (int32_t)1);
  197. data.minimumAlignment = std::max(data.minimumAlignment, alignment);
  198. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  199. data.naturalAlignment = std::max({ data.naturalAlignment, sa.alignment, sa.naturalAlignment });
  200. #endif
  201. data.nativeSize += sa.size;
  202. }
  203. data.classSize = AlignTo(data.actualClassSize, data.minimumAlignment);
  204. // C++ ABI difference between MS and Clang
  205. #if IL2CPP_CXX_ABI_MSVC
  206. data.actualClassSize = data.classSize;
  207. #endif
  208. }
  209. inline bool IsRawNormalStaticField(const Il2CppType* type, int32_t offset)
  210. {
  211. if ((type->attrs & FIELD_ATTRIBUTE_STATIC) == 0)
  212. return false;
  213. if (offset == THREAD_LOCAL_STATIC_MASK)
  214. return false;
  215. if ((type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0)
  216. return false;
  217. return true;
  218. }
  219. inline bool IsRawThreadStaticField(const Il2CppType* type, int32_t offset)
  220. {
  221. if ((type->attrs & FIELD_ATTRIBUTE_STATIC) == 0)
  222. return false;
  223. if (offset != THREAD_LOCAL_STATIC_MASK)
  224. return false;
  225. if ((type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0)
  226. return false;
  227. return true;
  228. }
  229. void ClassFieldLayoutCalculator::CalcClassNotStaticFields(const Il2CppType* type)
  230. {
  231. auto it = _classMap.find(type);
  232. if (it != _classMap.end())
  233. {
  234. return;
  235. }
  236. ClassLayoutInfo& layout = *(_classMap[type] = new (HYBRIDCLR_MALLOC_ZERO(sizeof(ClassLayoutInfo))) ClassLayoutInfo());
  237. layout.type = type;
  238. const Il2CppTypeDefinition* typeDef = GetUnderlyingTypeDefinition(type);
  239. const char* typeName = il2cpp::vm::GlobalMetadata::GetStringFromIndex(typeDef->nameIndex);
  240. std::vector<FieldLayout>& fields = layout.fields;
  241. fields.resize(typeDef->field_count, {});
  242. bool isCurAssemblyType = DecodeImageIndex(typeDef->byvalTypeIndex) == _image->GetIndex();
  243. if ((type->type == IL2CPP_TYPE_VALUETYPE || type->type == IL2CPP_TYPE_CLASS) && !isCurAssemblyType)
  244. {
  245. Il2CppClass* klass = il2cpp::vm::Class::FromIl2CppType(type);
  246. il2cpp::vm::Class::SetupFields(klass);
  247. layout.instanceSize = klass->instance_size;
  248. layout.actualSize = klass->actualSize;
  249. layout.nativeSize = klass->native_size;
  250. layout.alignment = klass->minimumAlignment;
  251. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  252. layout.naturalAlignment = klass->naturalAligment;
  253. #endif
  254. return;
  255. }
  256. const Il2CppGenericContext* gc = type->type == IL2CPP_TYPE_GENERICINST ? &type->data.generic_class->context : nullptr;
  257. for (uint16_t i = 0; i < typeDef->field_count; i++)
  258. {
  259. Il2CppFieldDefinition* fieldDef = (Il2CppFieldDefinition*)il2cpp::vm::GlobalMetadata::GetFieldDefinitionFromTypeDefAndFieldIndex(typeDef, i);
  260. const Il2CppType* fieldType = il2cpp::vm::GlobalMetadata::GetIl2CppTypeFromIndex(fieldDef->typeIndex);
  261. const Il2CppType* inflatedFieldType = gc ? TryInflateIfNeed(fieldType, gc, true) : fieldType;
  262. FieldLayout& fieldLayout = fields[i];
  263. fieldLayout.type = inflatedFieldType;
  264. if (isCurAssemblyType)
  265. {
  266. int32_t offset = _image->GetFieldOffset(typeDef, i);
  267. fieldLayout.offset = offset;
  268. fieldLayout.isNormalStatic = IsRawNormalStaticField(inflatedFieldType, offset);
  269. fieldLayout.isThreadStatic = IsRawThreadStaticField(inflatedFieldType, offset);
  270. }
  271. else
  272. {
  273. Il2CppClass* klass = il2cpp::vm::GlobalMetadata::GetTypeInfoFromHandle((Il2CppMetadataTypeHandle)typeDef);
  274. il2cpp::vm::Class::SetupFields(klass);
  275. FieldInfo* fieldInfo = klass->fields + i;
  276. fieldLayout.offset = fieldInfo->offset;
  277. fieldLayout.isNormalStatic = il2cpp::vm::Field::IsNormalStatic(fieldInfo);
  278. fieldLayout.isThreadStatic = il2cpp::vm::Field::IsThreadStatic(fieldInfo);
  279. }
  280. }
  281. if (il2cpp::metadata::GenericMetadata::ContainsGenericParameters(type)
  282. || ((type->type == IL2CPP_TYPE_VALUETYPE || type->type == IL2CPP_TYPE_CLASS) && typeDef->genericContainerIndex != kGenericContainerIndexInvalid))
  283. {
  284. layout.instanceSize = 0;
  285. layout.actualSize = 0;
  286. layout.nativeSize = -1;
  287. layout.alignment = 1;
  288. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  289. layout.naturalAlignment = 1;
  290. #endif
  291. return;
  292. }
  293. TbClassLayout classLayoutData = _image->GetClassLayout(typeDef);
  294. uint8_t packingSize = (uint8_t)classLayoutData.packingSize;
  295. int32_t classSize = (int32_t)(classLayoutData.classSize + sizeof(Il2CppObject));
  296. std::vector<FieldLayout*> instanceFields;
  297. for (FieldLayout& field : fields)
  298. {
  299. if (IsInstanceField(field.type))
  300. instanceFields.push_back(&field);
  301. }
  302. bool isExplictLayout = typeDef->flags & TYPE_ATTRIBUTE_EXPLICIT_LAYOUT;
  303. if (isExplictLayout)
  304. {
  305. IL2CPP_ASSERT(IsValueType(typeDef));
  306. IL2CPP_ASSERT(isCurAssemblyType);
  307. int32_t instanceSize = IL2CPP_SIZEOF_STRUCT_WITH_NO_INSTANCE_FIELDS + sizeof(Il2CppObject);
  308. if (classLayoutData.classSize > 0)
  309. {
  310. instanceSize = std::max(instanceSize, (int32_t)classLayoutData.classSize + (int32_t)sizeof(Il2CppObject));
  311. }
  312. int32_t maxAlignment = 1;
  313. int32_t nativeSize = 1;
  314. for (FieldLayout* field : instanceFields)
  315. {
  316. SizeAndAlignment sa = GetTypeSizeAndAlignment(field->type);
  317. instanceSize = std::max(instanceSize, field->offset + (int32_t)sa.size);
  318. maxAlignment = std::max(maxAlignment, (int32_t)sa.alignment);
  319. if (packingSize != 0)
  320. {
  321. maxAlignment = std::min(maxAlignment, (int32_t)packingSize);
  322. }
  323. nativeSize = AlignTo(std::max(nativeSize, field->offset + sa.nativeSize - (int32_t)sizeof(Il2CppObject)), maxAlignment);
  324. }
  325. layout.alignment = maxAlignment;
  326. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  327. layout.naturalAlignment = layout.alignment;
  328. #endif
  329. layout.actualSize = layout.instanceSize = AlignTo(instanceSize, layout.alignment);
  330. layout.nativeSize = nativeSize;
  331. if (classLayoutData.classSize > 0)
  332. {
  333. layout.actualSize = std::max(layout.actualSize, classSize);
  334. layout.instanceSize = std::max(layout.instanceSize, classSize);
  335. layout.nativeSize = std::max((int32_t)classLayoutData.classSize, layout.nativeSize);
  336. }
  337. }
  338. else
  339. {
  340. uint8_t parentMinimumAligment;
  341. int32_t parentActualSize = 0;
  342. bool isValueType = IsValueType(typeDef);
  343. if (typeDef->parentIndex != kInvalidIndex)
  344. {
  345. if (isValueType)
  346. {
  347. parentMinimumAligment = 1;
  348. parentActualSize = sizeof(Il2CppObject);
  349. }
  350. else
  351. {
  352. const Il2CppType* parentType = il2cpp::vm::GlobalMetadata::GetIl2CppTypeFromIndex(typeDef->parentIndex);
  353. parentType = TryInflateIfNeed(parentType, gc, true);
  354. CalcClassNotStaticFields(parentType);
  355. ClassLayoutInfo* parentLayout = GetClassLayoutInfo(parentType);
  356. parentActualSize = parentLayout->actualSize;
  357. parentMinimumAligment = parentLayout->alignment;
  358. }
  359. }
  360. else
  361. {
  362. parentActualSize = sizeof(Il2CppObject);
  363. parentMinimumAligment = PTR_SIZE;
  364. }
  365. FieldLayoutData layoutData;
  366. LayoutFields(parentActualSize, parentMinimumAligment, packingSize, instanceFields, layoutData);
  367. if (instanceFields.empty() && isValueType)
  368. {
  369. layoutData.classSize = layoutData.actualClassSize = IL2CPP_SIZEOF_STRUCT_WITH_NO_INSTANCE_FIELDS + sizeof(Il2CppObject);
  370. layoutData.nativeSize = IL2CPP_SIZEOF_STRUCT_WITH_NO_INSTANCE_FIELDS;
  371. }
  372. layout.alignment = layoutData.minimumAlignment;
  373. #if !HYBRIDCLR_UNITY_2022_OR_NEW
  374. layout.naturalAlignment = layoutData.naturalAlignment;
  375. #endif
  376. layout.actualSize = layoutData.actualClassSize;
  377. layout.instanceSize = layoutData.classSize;
  378. layout.nativeSize = AlignTo(layoutData.nativeSize, layout.alignment);
  379. if (!isValueType)
  380. {
  381. layout.nativeSize = -1;
  382. }
  383. if (classLayoutData.classSize > 0)
  384. {
  385. layout.actualSize = std::max(layout.actualSize, classSize);
  386. layout.instanceSize = std::max(layout.instanceSize, classSize);
  387. layout.nativeSize = isValueType ? std::max((int32_t)classLayoutData.classSize, layout.nativeSize) : -1;
  388. }
  389. }
  390. }
  391. void ClassFieldLayoutCalculator::CalcClassStaticFields(const Il2CppType* type)
  392. {
  393. IL2CPP_ASSERT(_classMap.find(type) != _classMap.end());
  394. ClassLayoutInfo& layout = *_classMap[type];
  395. std::vector<FieldLayout*> staticFields;
  396. std::vector<FieldLayout*> threadStaticFields;
  397. for (FieldLayout& field : layout.fields)
  398. {
  399. if (field.isNormalStatic)
  400. {
  401. staticFields.push_back(&field);
  402. }
  403. else if (field.isThreadStatic)
  404. {
  405. threadStaticFields.push_back(&field);
  406. }
  407. }
  408. if (!staticFields.empty())
  409. {
  410. FieldLayoutData staticLayoutData;
  411. LayoutFields(0, 1, 0, staticFields, staticLayoutData);
  412. layout.staticFieldsSize = staticLayoutData.classSize;
  413. }
  414. if (!threadStaticFields.empty())
  415. {
  416. FieldLayoutData threadStaticLayoutData;
  417. LayoutFields(0, 1, 0, threadStaticFields, threadStaticLayoutData);
  418. layout.threadStaticFieldsSize = threadStaticLayoutData.classSize;
  419. for (FieldLayout* field : threadStaticFields)
  420. {
  421. field->offset = field->offset | THREAD_LOCAL_STATIC_MASK;
  422. }
  423. }
  424. }
  425. }
  426. }