FoxIntermediateLinker.Mod 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. MODULE FoxIntermediateLinker;
  2. IMPORT
  3. Strings, Diagnostics, D := Debugging, SyntaxTree := FoxSyntaxTree, Sections := FoxSections,
  4. IntermediateCode := FoxIntermediateCode, Basic := FoxBasic, Streams, Files, Backend := FoxBackend,
  5. Global := FoxGlobal, Formats := FoxFormats, ActiveCells := FoxActiveCells,
  6. ObjectFile, BinaryCode := FoxBinaryCode, GenericLinker, StaticLinker, Commands, Options, IRObjectFile := FoxIntermediateObjectFile;
  7. CONST
  8. TraceLinking = FALSE;
  9. DefaultBackend = "AMD";
  10. TYPE
  11. FileName = ARRAY 256 OF CHAR;
  12. MessageString= ARRAY 256 OF CHAR;
  13. SectionName = ARRAY 256 OF CHAR; (*! move *)
  14. (** the assemblinker **)
  15. Linker = OBJECT
  16. CONST
  17. Trace = FALSE;
  18. RequireSortedSections = FALSE; (* whether the sections in the generated modules are sorted w.r.t. their fixed positions *)
  19. TYPE
  20. ArrangementRestriction = RECORD
  21. fixed: BOOLEAN;
  22. positionOrAlignment: LONGINT;
  23. END;
  24. VAR
  25. backend-: Backend.Backend;
  26. diagnostics: Diagnostics.Diagnostics;
  27. platformName, irFilePath: SyntaxTree.IdentifierString;
  28. importList, loadedModules: Sections.NameList;
  29. allSections: Sections.SectionList;
  30. isSorted, alreadyPrearrangedSinceLastSort: BOOLEAN;
  31. originalRestrictions: POINTER TO ARRAY OF ArrangementRestriction;
  32. objectFile: IRObjectFile.ObjectFileFormat;
  33. PROCEDURE & Init(diagnostics: Diagnostics.Diagnostics; defaultBackend: Backend.Backend; irFilePath: SyntaxTree.IdentifierString);
  34. BEGIN
  35. IF diagnostics = NIL THEN
  36. SELF.diagnostics := Basic.GetDefaultDiagnostics()
  37. ELSE
  38. SELF.diagnostics := diagnostics;
  39. END;
  40. SELF.irFilePath := irFilePath;
  41. backend := defaultBackend;
  42. defaultBackend.GetDescription(platformName);
  43. NEW(allSections);
  44. NEW(importList, 128);
  45. NEW(loadedModules, 128);
  46. NEW(objectFile); objectFile.Initialize(diagnostics,"");
  47. isSorted := FALSE
  48. END Init;
  49. PROCEDURE PatchStackSize(CONST typeName: SectionName; size: LONGINT);
  50. VAR sectionName: SectionName; section: Sections.Section; pooledName: Basic.SegmentedName; op1, op2, op3: IntermediateCode.Operand; instruction: IntermediateCode.Instruction;
  51. BEGIN
  52. TRACE(size);
  53. COPY(typeName, sectionName);
  54. Strings.Append(sectionName,".@StackAllocation");
  55. Basic.ToSegmentedName(sectionName, pooledName);
  56. section := allSections.FindByName(pooledName);
  57. instruction := section(IntermediateCode.Section).instructions[0];
  58. op1 := instruction.op1;
  59. op2 := instruction.op2;
  60. op3 := instruction.op3;
  61. IntermediateCode.SetIntValue(op2, size);
  62. section(IntermediateCode.Section).PatchOperands(0, op1, op2, op3);
  63. END PatchStackSize;
  64. PROCEDURE LoadModule(CONST moduleFileName: ARRAY OF CHAR; recursive: BOOLEAN): BOOLEAN;
  65. VAR
  66. filename, moduleName: SyntaxTree.IdentifierString;
  67. msg: ARRAY 128 OF CHAR;
  68. i: LONGINT;
  69. module: Sections.Module;
  70. name: SyntaxTree.IdentifierString;
  71. BEGIN
  72. FileNameToModuleName(moduleFileName, moduleName);
  73. (* check if the module has already been incorporated *)
  74. IF loadedModules.ContainsName(moduleName) THEN
  75. IF Trace THEN D.String(">>> module "); D.String(moduleName); D.String(" has already been loaded"); D.Ln END;
  76. RETURN TRUE
  77. ELSE
  78. IF moduleName = "SYSTEM" THEN
  79. (* nothing to do *)
  80. ELSE
  81. (* open corresponding intermediate code file *)
  82. module := objectFile.Import(moduleName, backend.GetSystem());
  83. IF module = NIL THEN
  84. msg := "failed to import IR file ";
  85. Strings.Append(msg, moduleFileName);
  86. diagnostics.Error(filename, Diagnostics.Invalid, Diagnostics.Invalid, msg);
  87. RETURN FALSE
  88. ELSE
  89. loadedModules.AddName(moduleName); (* to avoid recursive reloading this must be done before parsing *)
  90. IF recursive THEN
  91. FOR i := 0 TO module.imports.Length()-1 DO
  92. name := module.imports.GetName(i);
  93. IF ~LoadModule(name, recursive) THEN
  94. msg := "failed to import ";
  95. Strings.Append(msg, name);
  96. diagnostics.Error(filename, Diagnostics.Invalid, Diagnostics.Invalid, msg);
  97. RETURN FALSE
  98. END;
  99. END;
  100. END;
  101. CopySections(module.allSections, allSections);
  102. IF Trace THEN
  103. D.String(">>> IR file successfully parsed: "); D.String(filename); D.Ln;
  104. DumpSections(D.Log, allSections);
  105. END;
  106. isSorted := FALSE; (* sections are not sorted anymore *)
  107. RETURN TRUE
  108. END
  109. END;
  110. RETURN TRUE
  111. END
  112. END LoadModule;
  113. (** mark a section with a certain name as reachable **)
  114. PROCEDURE MarkAsReachableByName(CONST name: ARRAY OF CHAR);
  115. VAR
  116. section: Sections.Section;
  117. pooledName: Basic.SegmentedName;
  118. BEGIN
  119. Basic.ToSegmentedName(name, pooledName);
  120. section:= allSections.FindByName(pooledName);
  121. ASSERT(section # NIL);
  122. MarkAsReachable(section)
  123. END MarkAsReachableByName;
  124. (** mark all sections whose names start with a certain prefix as reachable **)
  125. PROCEDURE MarkAsReachableStartingWith(CONST prefix: Basic.SegmentedName; allowedSections: SET);
  126. VAR
  127. section: Sections.Section; name: Basic.SegmentedName;
  128. i: LONGINT;
  129. BEGIN
  130. (* TODO: could or should one make this faster using a hash table? *)
  131. (* go through all sections *)
  132. FOR i := 0 TO allSections.Length() - 1 DO
  133. section := allSections.GetSection(i);
  134. IF section.type IN allowedSections THEN
  135. IF Basic.IsPrefix(prefix, section.name) THEN
  136. name := section.name;
  137. Basic.RemoveSuffix(name);
  138. IF prefix = name THEN
  139. MarkAsReachable(section)
  140. END
  141. END
  142. END;
  143. END
  144. END MarkAsReachableStartingWith;
  145. PROCEDURE ModuleIsReachable(CONST name: Basic.String): BOOLEAN;
  146. VAR i: LONGINT; section: Sections.Section;
  147. BEGIN
  148. FOR i := 0 TO allSections.Length()-1 DO
  149. section := allSections.GetSection(i);
  150. IF (section.name[0] = name) & section.isReachable THEN
  151. RETURN TRUE
  152. END;
  153. END;
  154. RETURN FALSE
  155. END ModuleIsReachable;
  156. PROCEDURE OperandSection(CONST operand: IntermediateCode.Operand): Sections.Section;
  157. VAR section: Sections.Section;
  158. BEGIN
  159. section := allSections.FindByName(operand.symbol.name);
  160. IF section = NIL THEN D.String("not found section: "); Basic.WriteSegmentedName(D.Log, operand.symbol.name); D.Ln END;
  161. RETURN allSections.FindByName(operand.symbol.name);
  162. END OperandSection;
  163. (** mark a section as reachable and do the same recursively for all referenced sections **)
  164. PROCEDURE MarkAsReachable(section: Sections.Section);
  165. VAR
  166. intermediateCodeSection: IntermediateCode.Section;
  167. i: LONGINT;
  168. procedureName, moduleName: SyntaxTree.IdentifierString;
  169. prefix: Basic.SegmentedName;
  170. BEGIN
  171. IF ~section.isReachable THEN
  172. IF Trace THEN D.String(">>> MarkAsReachable "); Basic.WriteSegmentedName(D.Log, section.name); D.Ln END;
  173. section.SetReachability(TRUE);
  174. prefix := section.name; Basic.RemoveSuffix(prefix);
  175. MarkAsReachableStartingWith(prefix, {Sections.InitCodeSection});
  176. ASSERT(section IS IntermediateCode.Section);
  177. intermediateCodeSection := section(IntermediateCode.Section);
  178. (* go through all instructions in the section *)
  179. FOR i := 0 TO intermediateCodeSection.pc - 1 DO
  180. IF ~backend(IntermediateCode.IntermediateBackend).SupportedInstruction(intermediateCodeSection.instructions[i], moduleName, procedureName) THEN
  181. Strings.Append(moduleName,".");
  182. Strings.Append(moduleName, procedureName);
  183. MarkAsReachableByName(moduleName);
  184. END;
  185. IF intermediateCodeSection.instructions[i].op1.symbol.name # "" THEN MarkAsReachable(OperandSection(intermediateCodeSection.instructions[i].op1)) END;
  186. IF intermediateCodeSection.instructions[i].op2.symbol.name # "" THEN MarkAsReachable(OperandSection(intermediateCodeSection.instructions[i].op2)) END;
  187. IF intermediateCodeSection.instructions[i].op3.symbol.name # "" THEN MarkAsReachable(OperandSection(intermediateCodeSection.instructions[i].op3)) END
  188. END
  189. END
  190. END MarkAsReachable;
  191. (** mark all sections as either reachable or unreachable **)
  192. PROCEDURE MarkReachabilityOfAll(isReachable: BOOLEAN);
  193. VAR
  194. section: Sections.Section;
  195. i: LONGINT;
  196. BEGIN
  197. IF Trace THEN D.String(">>> MarkReachabilityOfAll "); IF isReachable THEN D.String("TRUE") ELSE D.String("FALSE") END; D.Ln END;
  198. FOR i := 0 TO allSections.Length() - 1 DO
  199. section := allSections.GetSection(i);
  200. section.SetReachability(isReachable)
  201. END
  202. END MarkReachabilityOfAll;
  203. (** dump all sections (both reachable and not) **)
  204. PROCEDURE DumpSections(writer: Streams.Writer; sections: Sections.SectionList);
  205. VAR
  206. section: Sections.Section;
  207. i: LONGINT;
  208. BEGIN
  209. FOR i := 0 TO sections.Length() - 1 DO
  210. section := sections.GetSection(i);
  211. IF section.isReachable THEN
  212. writer.String("REACHABLE ")
  213. ELSE
  214. writer.String("unreachable ")
  215. END;
  216. section.Dump(writer)
  217. END;
  218. writer.Update
  219. END DumpSections;
  220. (** store the original arrangment restrictions of all sections **)
  221. PROCEDURE StoreOriginalRestrictions;
  222. VAR
  223. section: Sections.Section;
  224. i: LONGINT;
  225. BEGIN
  226. NEW(originalRestrictions, allSections.Length());
  227. FOR i := 0 TO allSections.Length() - 1 DO
  228. section := allSections.GetSection(i);
  229. originalRestrictions[i].fixed := section.fixed;
  230. originalRestrictions[i].positionOrAlignment := section.positionOrAlignment
  231. END
  232. END StoreOriginalRestrictions;
  233. (** restore the original arrangment restrictions of all sections **)
  234. PROCEDURE RestoreOriginalRestrictions;
  235. VAR
  236. section: Sections.Section;
  237. i: LONGINT;
  238. BEGIN
  239. ASSERT(LEN(originalRestrictions) = allSections.Length());
  240. FOR i := 0 TO allSections.Length() - 1 DO
  241. section := allSections.GetSection(i);
  242. section.SetPositionOrAlignment(originalRestrictions[i].fixed, originalRestrictions[i].positionOrAlignment)
  243. END
  244. END RestoreOriginalRestrictions;
  245. PROCEDURE PrearrangeReachableDataSections;
  246. VAR
  247. fixedDataSections, flexibleDataSections: Sections.SectionList;
  248. section, fixedDataSection, flexibleDataSection: Sections.Section;
  249. i, currentAddress, nextOccupiedAddress, flexibleDataSectionIndex, fixedDataSectionIndex, startAddress, endAddress: LONGINT;
  250. done: BOOLEAN;
  251. BEGIN
  252. (* sort sections if necessary *)
  253. IF ~isSorted THEN
  254. IF Trace THEN D.String("++++++++++ before sorting ++++++++++"); DumpSections(D.Log, allSections) END;
  255. FOR i:= 0 TO allSections.Length() - 1 DO
  256. allSections.GetSection(i).SetOffset(i)
  257. END;
  258. allSections.Sort(SectionPositionAndSizeComparison);
  259. IF Trace THEN D.String("++++++++++ after sorting ++++++++++"); DumpSections(D.Log, allSections) END;
  260. isSorted := TRUE;
  261. alreadyPrearrangedSinceLastSort := FALSE
  262. END;
  263. ASSERT(isSorted);
  264. IF alreadyPrearrangedSinceLastSort THEN RestoreOriginalRestrictions ELSE StoreOriginalRestrictions END;
  265. IF Trace THEN D.String("before prearrangement"); D.Ln; DumpSections(D.Log, allSections); D.Ln END;
  266. (* create new lists for reachable data sections that are fixed or flexible, respectively *)
  267. NEW(fixedDataSections);
  268. NEW(flexibleDataSections);
  269. (* go through all reachable data sections, and put them into one of two lists *)
  270. FOR i:= 0 TO allSections.Length() - 1 DO
  271. section := allSections.GetSection(i);
  272. IF section.isReachable & ((section.type = Sections.ConstSection) OR (section.type = Sections.VarSection)) THEN
  273. IF section.fixed THEN
  274. fixedDataSections.AddSection(section)
  275. ELSE
  276. flexibleDataSections.AddSection(section)
  277. END
  278. END
  279. END;
  280. IF Trace THEN
  281. D.String("++++++++++ reachable fixed data sections ++++++++++"); fixedDataSections.Dump(D.Log); D.Ln;
  282. D.String("++++++++++ reachable flexible data sections ++++++++++"); flexibleDataSections.Dump(D.Log); D.Ln;
  283. END;
  284. (* arrange the sections (i.e. set the fixed attribute) such that the given fixed-positions and alignments are respected *)
  285. currentAddress := 0;
  286. flexibleDataSectionIndex := 0;
  287. (* go through all fixed data sections of the cell *)
  288. FOR fixedDataSectionIndex := 0 TO fixedDataSections.Length() DO (* note: the index may be out-of-bounds! *)
  289. IF fixedDataSectionIndex < fixedDataSections.Length() THEN
  290. fixedDataSection := fixedDataSections.GetSection(fixedDataSectionIndex);
  291. ASSERT(fixedDataSection.fixed);
  292. nextOccupiedAddress := fixedDataSection.positionOrAlignment
  293. ELSE
  294. (* there is no more fixed data section *)
  295. nextOccupiedAddress := MAX(LONGINT)
  296. END;
  297. done := FALSE;
  298. WHILE ~done DO
  299. IF flexibleDataSectionIndex < flexibleDataSections.Length() THEN
  300. flexibleDataSection := flexibleDataSections.GetSection(flexibleDataSectionIndex);
  301. (* determine start-address of the next section (respect alignment) *)
  302. IF flexibleDataSection.IsAligned() & ((currentAddress MOD flexibleDataSection.positionOrAlignment) # 0) THEN
  303. startAddress := currentAddress + flexibleDataSection.positionOrAlignment - (currentAddress MOD flexibleDataSection.positionOrAlignment)
  304. ELSE
  305. startAddress := currentAddress
  306. END;
  307. (* determine end-address fo the next section *)
  308. endAddress := startAddress + flexibleDataSection.GetSize();
  309. IF endAddress <= nextOccupiedAddress THEN
  310. (* there is enough space for the section *)
  311. flexibleDataSection.SetPositionOrAlignment(TRUE, startAddress); (* position is set for section *)
  312. INC(flexibleDataSectionIndex);
  313. currentAddress := endAddress
  314. ELSE
  315. (* there is no more space for sections *)
  316. done := TRUE
  317. END
  318. ELSE
  319. (* there are no more flexible data sections *)
  320. done := TRUE
  321. END
  322. END;
  323. IF fixedDataSectionIndex < fixedDataSections.Length() THEN
  324. ASSERT(fixedDataSection.GetSize() # Sections.UnknownSize);
  325. currentAddress := fixedDataSection.positionOrAlignment + fixedDataSection.GetSize()
  326. END
  327. END;
  328. alreadyPrearrangedSinceLastSort := TRUE;
  329. IF Trace THEN D.String("after prearrangement"); D.Ln; DumpSections(D.Log, allSections); D.Ln END;
  330. END PrearrangeReachableDataSections;
  331. PROCEDURE PatchValueInSection*(CONST sectionName: Basic.SegmentedName; syntaxTreeValue: SyntaxTree.Value);
  332. VAR
  333. section: Sections.Section;
  334. emptyOperand, dataOperand: IntermediateCode.Operand;
  335. dataInstruction: IntermediateCode.Instruction;
  336. hugeintValue: HUGEINT;
  337. BEGIN
  338. section := allSections.FindByName(sectionName);
  339. ASSERT(section # NIL);
  340. IF syntaxTreeValue IS SyntaxTree.BooleanValue THEN
  341. (* BOOLEAN *)
  342. IF syntaxTreeValue(SyntaxTree.BooleanValue).value THEN hugeintValue := 1 ELSE hugeintValue := 0 END
  343. ELSIF syntaxTreeValue IS SyntaxTree.IntegerValue THEN
  344. (* INTEGER *)
  345. hugeintValue := syntaxTreeValue(SyntaxTree.IntegerValue).hvalue;
  346. ELSE
  347. HALT(100)
  348. END;
  349. IntermediateCode.InitImmediate(dataOperand, IntermediateCode.GetType(backend.GetSystem(), syntaxTreeValue.type.resolved), hugeintValue);
  350. IntermediateCode.InitOperand(emptyOperand);
  351. IntermediateCode.InitInstruction(dataInstruction, -1, IntermediateCode.data, dataOperand, emptyOperand, emptyOperand);
  352. ASSERT(section IS IntermediateCode.Section);
  353. section(IntermediateCode.Section).EmitAt(0, dataInstruction)
  354. END PatchValueInSection;
  355. (** get all reachable sections in the form of an intermediate code module with a certain name **)
  356. PROCEDURE ExtractModuleWithName(CONST desiredName: ARRAY OF CHAR): Sections.Module;
  357. VAR
  358. result: Sections.Module;
  359. section: Sections.Section;
  360. i: LONGINT;
  361. BEGIN
  362. NEW(result, NIL, backend.GetSystem()); (* note: there is no syntax tree *)
  363. result.SetModuleName(desiredName);
  364. result.SetPlatformName(platformName);
  365. result.SetImports(importList);
  366. (* add all of the reachable sections from the cumulative section list into the resulting module's section list *)
  367. FOR i := 0 TO allSections.Length() - 1 DO
  368. section := allSections.GetSection(i);
  369. (* remove any previously generated code *)
  370. ASSERT(section IS IntermediateCode.Section);
  371. section(IntermediateCode.Section).SetResolved(NIL);
  372. IF section.isReachable THEN result.allSections.AddSection(section) END
  373. END;
  374. IF RequireSortedSections THEN result.allSections.Sort(SectionPositionComparison) END;
  375. IF Trace THEN D.String("+++++++++ intermediate code module ++++++++++"); D.Ln; result.Dump(D.Log); D.Ln; END;
  376. RETURN result
  377. END ExtractModuleWithName;
  378. PROCEDURE SectionPositionComparison(leftObject, rightObject: ANY): BOOLEAN;
  379. VAR
  380. leftSection, rightSection: Sections.Section;
  381. leftPosition, rightPosition: LONGINT;
  382. BEGIN
  383. ASSERT((leftObject IS Sections.Section) & (rightObject IS Sections.Section));
  384. leftSection := leftObject(Sections.Section);
  385. rightSection := rightObject(Sections.Section);
  386. IF leftSection.fixed THEN
  387. leftPosition := leftSection.positionOrAlignment
  388. ELSE
  389. leftPosition := MAX(LONGINT)
  390. END;
  391. IF rightSection.fixed THEN
  392. rightPosition := rightSection.positionOrAlignment
  393. ELSE
  394. rightPosition := MAX(LONGINT)
  395. END;
  396. IF leftSection.IsCode() & rightSection.IsCode() THEN RETURN FALSE END;
  397. RETURN leftPosition < rightPosition
  398. END SectionPositionComparison;
  399. (** whether a section should appear before another one in an assembly (used for sorting)
  400. - 1st priority: when sections have fixed positions, the ones with smaller addresses come first
  401. - 2nd priority: smaller sections come first
  402. **)
  403. PROCEDURE SectionPositionAndSizeComparison(leftObject, rightObject: ANY): BOOLEAN;
  404. VAR
  405. leftSection, rightSection: Sections.Section;
  406. leftPosition, rightPosition, leftSize, rightSize: LONGINT;
  407. BEGIN
  408. ASSERT((leftObject IS Sections.Section) & (rightObject IS Sections.Section));
  409. leftSection := leftObject(Sections.Section);
  410. rightSection := rightObject(Sections.Section);
  411. IF leftSection.fixed THEN
  412. leftPosition := leftSection.positionOrAlignment
  413. ELSE
  414. leftPosition := MAX(LONGINT)
  415. END;
  416. IF rightSection.fixed THEN
  417. rightPosition := rightSection.positionOrAlignment
  418. ELSE
  419. rightPosition := MAX(LONGINT)
  420. END;
  421. IF ~leftSection.IsCode() & rightSection.IsCode() THEN (* data sections first *)
  422. RETURN TRUE
  423. ELSIF leftSection.IsCode() & ~rightSection.IsCode() THEN (* data sections first *)
  424. RETURN FALSE
  425. ELSIF leftSection.IsCode() & rightSection.IsCode() THEN (* code sections: sorted by linking preference, stable w.r.t. loading order *)
  426. IF GetPriority(leftSection) < GetPriority(rightSection) THEN
  427. RETURN TRUE
  428. ELSIF GetPriority(leftSection) = GetPriority(rightSection) THEN
  429. RETURN (leftSection.priority < rightSection.priority) OR (leftSection.priority = rightSection.priority) & (leftSection.offset < rightSection.offset) (* must keep order as provided by loader *)
  430. ELSE
  431. RETURN FALSE
  432. END
  433. ELSIF leftPosition < rightPosition THEN (* data sections sorted by position *)
  434. RETURN TRUE
  435. ELSIF leftPosition > rightPosition THEN (* data sections sorted by position *)
  436. RETURN FALSE
  437. ELSE (* data section sorted by size, if no position provided *)
  438. ASSERT(leftPosition = rightPosition); (* note: this is the case for sections without fixed positions *)
  439. leftSize := leftSection.GetSize();
  440. rightSize := rightSection.GetSize();
  441. IF (leftSize = Sections.UnknownSize) OR (leftSize = 0) THEN leftSize := MAX(LONGINT) END;
  442. IF (rightSize = Sections.UnknownSize) OR (rightSize = 0) THEN rightSize := MAX(LONGINT) END;
  443. IF leftSize = rightSize THEN
  444. RETURN leftSection.offset < rightSection.offset (* keeping order as provided by loader, cosmetic *)
  445. ELSE
  446. RETURN leftSize < rightSize
  447. END
  448. END
  449. END SectionPositionAndSizeComparison;
  450. (* set address of sections to a fixed position after compilation *)
  451. PROCEDURE FixSections(binaryModule: Sections.Module; VAR sizes: ARRAY OF LONGINT);
  452. VAR adr,i: LONGINT; section: Sections.Section; is: BinaryCode.Section;
  453. BEGIN
  454. adr := 0;
  455. FOR i := 0 TO binaryModule.allSections.Length()-1 DO
  456. section := binaryModule.allSections.GetSection(i);
  457. is := section(IntermediateCode.Section).resolved;
  458. IF (is # NIL) & section.IsCode() THEN
  459. (*
  460. Basic.WriteSegmentedName(D.Log,section.name);
  461. D.String(" @ "); D.Int(adr,1); D.Ln;
  462. *)
  463. backend.CheckCodeAddress(adr);
  464. is.SetAlignment(TRUE, adr);
  465. IF is.pc > sizes[i] THEN sizes[i] := is.pc END;
  466. adr := adr + sizes[i];
  467. END;
  468. is.Reset; (* enable recompilation *)
  469. END;
  470. END FixSections;
  471. (* check if any of the addresses of sections have changed during last compilation *)
  472. PROCEDURE Conflict(binaryModule: Sections.Module; VAR sizes: ARRAY OF LONGINT): BOOLEAN;
  473. VAR adr,i: LONGINT; section: Sections.Section;is: BinaryCode.Section;
  474. BEGIN
  475. adr := 0;
  476. FOR i := 0 TO binaryModule.allSections.Length()-1 DO
  477. section := binaryModule.allSections.GetSection(i);
  478. is := section(IntermediateCode.Section).resolved;
  479. IF (is # NIL) & section.IsCode() THEN
  480. IF is.pc > sizes[i] THEN RETURN TRUE
  481. (*
  482. not necessary, the linker places correctly.
  483. ELSIF is.pc < sizes[i] THEN is.SetPC(sizes[i]) (* set section size to maximal observed size *)
  484. *)
  485. END;
  486. END;
  487. END;
  488. RETURN FALSE
  489. END Conflict;
  490. (* generate binary code and write an object file with a desired module name *)
  491. PROCEDURE GenerateObjectFile*(objectFileFormat: Formats.ObjectFileFormat; log: Streams.Writer; CONST desiredName: ARRAY OF CHAR): BOOLEAN;
  492. VAR
  493. count: LONGINT;
  494. intermediateCodeModule: Sections.Module;
  495. binaryModule: Formats.GeneratedModule;
  496. result: BOOLEAN;
  497. sizes: POINTER TO ARRAY OF LONGINT; i: LONGINT;
  498. objectFileExtension: ARRAY 32 OF CHAR; objectFileName: Files.FileName;
  499. BEGIN
  500. intermediateCodeModule := ExtractModuleWithName(desiredName);
  501. result := TRUE;
  502. (* generate binary code *)
  503. backend.Initialize(diagnostics, log, {}, NIL, backend.GetSystem(), NIL);
  504. binaryModule := backend.ProcessIntermediateCodeModule(intermediateCodeModule); count := 0;
  505. (* iterative compilation until all sections remain fixed at their position *)
  506. NEW(sizes, binaryModule(Sections.Module).allSections.Length());
  507. FOR i := 0 TO LEN(sizes)-1 DO sizes[i] := 0 END;
  508. REPEAT
  509. INC(count);
  510. (* fix all section addresses *)
  511. FixSections(binaryModule(Sections.Module),sizes^);
  512. (* compile *)
  513. binaryModule := backend.ProcessIntermediateCodeModule(intermediateCodeModule);
  514. (* and repeat if any of the section addresses have to be adapted *)
  515. UNTIL ~Conflict(binaryModule(Sections.Module),sizes^) OR (count > 10) ;
  516. ASSERT(count <=10);
  517. IF binaryModule = NIL THEN
  518. diagnostics.Error(desiredName, Diagnostics.Invalid, Diagnostics.Invalid, "the specified backend cannot process intermediate code");
  519. result := FALSE
  520. ELSIF backend.error THEN
  521. diagnostics.Error(desiredName, Diagnostics.Invalid, Diagnostics.Invalid, "binary code could not be generated (backend error)");
  522. result := FALSE
  523. ELSE
  524. IF Trace THEN D.String(">>> binary code successfully generated"); D.Ln END;
  525. IF objectFileFormat = NIL THEN
  526. diagnostics.Error(desiredName, Diagnostics.Invalid, Diagnostics.Invalid, "no object file format specified");
  527. result := FALSE
  528. ELSE
  529. (* write the generated code into an object file *)
  530. objectFileFormat.Initialize(diagnostics,"");
  531. IF objectFileFormat.Export(binaryModule, NIL) THEN
  532. IF log # NIL THEN
  533. log.String("assembled "); log.String(desiredName); log.String(" => ");
  534. objectFileFormat.GetExtension(objectFileExtension);
  535. Files.JoinExtension(desiredName, objectFileExtension, objectFileName);
  536. log.String(objectFileName); log.Ln;
  537. END;
  538. IF Trace THEN D.String(">>> object file successfully written"); D.Ln END;
  539. ELSE
  540. diagnostics.Error(desiredName, Diagnostics.Invalid, Diagnostics.Invalid, "object file could not be written");
  541. result := FALSE
  542. END
  543. END
  544. END;
  545. RETURN result
  546. END GenerateObjectFile;
  547. END Linker;
  548. SpecificationLinker=OBJECT (Backend.Backend)
  549. VAR objectFileFormat: Formats.ObjectFileFormat;
  550. PROCEDURE &Init;
  551. BEGIN
  552. InitBackend;
  553. objectFileFormat := Formats.GetObjectFileFormat("Generic");
  554. END Init;
  555. PROCEDURE Emit(backend: Backend.Backend): BOOLEAN;
  556. BEGIN
  557. RETURN LinkActiveCells(activeCellsSpecification, backend, objectFileFormat);
  558. END Emit;
  559. PROCEDURE DefineOptions(options: Options.Options);
  560. BEGIN
  561. objectFileFormat.DefineOptions(options);
  562. END DefineOptions;
  563. PROCEDURE GetOptions(options: Options.Options);
  564. BEGIN
  565. objectFileFormat.GetOptions(options);
  566. END GetOptions;
  567. END SpecificationLinker;
  568. PROCEDURE Get*(): Backend.Backend;
  569. VAR backend: SpecificationLinker;
  570. BEGIN
  571. NEW(backend); RETURN backend
  572. END Get;
  573. PROCEDURE GetPriority*(block: Sections.Section): LONGINT;
  574. CONST Fixed=0; InitCode=1; BodyCode=2;Code=3; Data=4; Const=5; Empty =6;
  575. BEGIN
  576. IF block.fixed THEN RETURN Fixed END;
  577. IF block.type = ObjectFile.InitCode THEN RETURN InitCode END;
  578. IF block.type = ObjectFile.BodyCode THEN RETURN Code END; (* BodyCode does not necessarily have to be in front of code *)
  579. IF block.GetSize () = 0 THEN RETURN Empty END;
  580. IF block.type = ObjectFile.Code THEN RETURN Code END;
  581. IF block.type = ObjectFile.Data THEN RETURN Code END;
  582. IF block.type = ObjectFile.Const THEN RETURN Code END;
  583. HALT(100); (* undefined type *)
  584. END GetPriority;
  585. PROCEDURE CopySections*(from, to: Sections.SectionList);
  586. VAR section, copy: IntermediateCode.Section; i,j: LONGINT; s: Sections.Section; instruction: IntermediateCode.Instruction;
  587. BEGIN
  588. FOR i := 0 TO from.Length()-1 DO
  589. s := from.GetSection(i);
  590. section := s(IntermediateCode.Section);
  591. copy := IntermediateCode.NewSection(to, section.type, section.name, NIL, FALSE);
  592. copy.SetBitsPerUnit(section.bitsPerUnit);
  593. copy.SetPositionOrAlignment(section.fixed, section.positionOrAlignment);
  594. copy.SetFingerprint(section.fingerprint);
  595. copy.SetPriority(section.priority);
  596. FOR j := 0 TO section.pc-1 DO
  597. instruction := section.instructions[j];
  598. copy.Emit(instruction);
  599. END;
  600. END;
  601. END CopySections;
  602. PROCEDURE FileNameToModuleName(CONST filename: ARRAY OF CHAR; VAR moduleName: ARRAY OF CHAR);
  603. VAR extension: FileName;
  604. BEGIN
  605. Files.SplitExtension(filename, moduleName, extension);
  606. END FileNameToModuleName;
  607. PROCEDURE LinkActiveCells*(activeCellsSpecification: ActiveCells.Specification; backend: Backend.Backend; objectFileFormat: Formats.ObjectFileFormat): BOOLEAN;
  608. TYPE
  609. LinkerObject= OBJECT
  610. VAR
  611. specification: ActiveCells.Specification;
  612. backend: Backend.Backend;
  613. diagnostics: Diagnostics.Diagnostics;
  614. irLinker: Linker;
  615. objectFileFormat: Formats.ObjectFileFormat;
  616. error: BOOLEAN;
  617. system: Global.System;
  618. PROCEDURE &Init(activeCellsSpecification: ActiveCells.Specification; b: Backend.Backend; objectFileFormat: Formats.ObjectFileFormat);
  619. BEGIN
  620. error := FALSE;
  621. SELF.specification := activeCellsSpecification;
  622. SELF.backend := b;
  623. SELF.diagnostics := specification.diagnostics;
  624. IF diagnostics = NIL THEN diagnostics := Basic.GetDefaultDiagnostics() END;
  625. SELF.objectFileFormat := objectFileFormat;
  626. NEW(irLinker, specification.diagnostics, backend, ""); (* TODO: pass an optional path as third parameter *)
  627. IF ~irLinker.LoadModule(backend(IntermediateCode.IntermediateBackend).runtimeModuleName, TRUE) THEN
  628. error := TRUE;
  629. diagnostics.Error(backend(IntermediateCode.IntermediateBackend).runtimeModuleName,Diagnostics.Invalid, Diagnostics.Invalid, "could not load ir file");
  630. END;
  631. IF ~irLinker.LoadModule(specification.name,TRUE) THEN
  632. error := TRUE;
  633. diagnostics.Error(specification.name,Diagnostics.Invalid, Diagnostics.Invalid, "could not load ir file");
  634. END;
  635. backend := irLinker.backend;
  636. system := backend.system;
  637. END Init;
  638. PROCEDURE LinkInstance(instance: ActiveCells.Instance): BOOLEAN;
  639. VAR
  640. codeFileName, dataFileName: Files.FileName;
  641. typeName, instanceName, linkRoot: SectionName;
  642. code, data: StaticLinker.Arrangement; linker: GenericLinker.Linker;
  643. i: LONGINT;
  644. logFile: Files.File; linkerLog: Files.Writer;
  645. type: ActiveCells.Type;
  646. msg: MessageString;
  647. objectFileExtension: ARRAY 32 OF CHAR;
  648. instructionMemorySize, dataMemorySize: LONGINT;
  649. parameter: ActiveCells.Parameter;
  650. value: SyntaxTree.Value;
  651. pooledName: Basic.SegmentedName;
  652. device: ActiveCells.Device;
  653. error : BOOLEAN;
  654. CONST MinimalStackSize=64;
  655. BEGIN
  656. error := FALSE;
  657. type := instance.instanceType;
  658. type.GetFullName(typeName,NIL);
  659. instance.GetFullName(instanceName,NIL);
  660. IF TraceLinking THEN
  661. D.String("assembling instance "); D.String(instanceName); D.String(" of type "); D.String(typeName); D.Ln;
  662. END;
  663. IF instance.IsEngine() THEN
  664. IF TraceLinking THEN
  665. D.String("instance "); D.String(instanceName); D.String(" is engine "); D.Ln;
  666. END;
  667. RETURN TRUE;
  668. END;
  669. backend.SetCapabilities(instance.capabilities);
  670. irLinker.MarkReachabilityOfAll(FALSE);
  671. COPY(typeName, linkRoot);
  672. Strings.Append(linkRoot,".@BodyStub");
  673. irLinker.MarkAsReachableByName(linkRoot);
  674. irLinker.PatchStackSize(typeName, instance.dataMemorySize);
  675. FOR i := 0 TO instance.parameters.Length()-1 DO
  676. parameter := instance.parameters.GetParameter(i);
  677. IF parameter.parameterType = 0 THEN (* Boolean *)
  678. value := SyntaxTree.NewBooleanValue(-1, parameter.boolean); value.SetType(system.booleanType);
  679. ELSE
  680. value := SyntaxTree.NewIntegerValue(-1, parameter.integer); value.SetType(system.integerType);
  681. END;
  682. Basic.ToSegmentedName(parameter.name, pooledName);
  683. irLinker.PatchValueInSection(pooledName,value);
  684. END;
  685. FOR i := 0 TO type.specification.supportedDevices.Length()-1 DO
  686. device := type.specification.supportedDevices.GetDevice(i);
  687. IF instance.instanceType.devices.ByName(device.name) = NIL THEN
  688. IF irLinker.ModuleIsReachable(Basic.MakeString(device.name)) THEN
  689. msg := "Missing device capability ";
  690. Strings.Append(msg, device.name);
  691. Strings.Append(msg," in cell ");
  692. instance.AppendToMsg(msg);
  693. diagnostics.Error(specification.name,Diagnostics.Invalid, Diagnostics.Invalid, msg);
  694. error := TRUE;
  695. END;
  696. ELSE
  697. IF ~irLinker.ModuleIsReachable(Basic.MakeString(device.name)) THEN
  698. msg := "Unused device ";
  699. Strings.Append(msg, device.name);
  700. Strings.Append(msg," in cell ");
  701. instance.AppendToMsg(msg);
  702. diagnostics.Warning(specification.name,Diagnostics.Invalid,Diagnostics.Invalid,msg);
  703. END;
  704. END;
  705. END;
  706. IF error THEN RETURN FALSE END;
  707. objectFileFormat.GetExtension(objectFileExtension);
  708. irLinker.PrearrangeReachableDataSections;
  709. IF ~irLinker.GenerateObjectFile(objectFileFormat, specification.log, instanceName) THEN
  710. diagnostics.Error(specification.name,Diagnostics.Invalid, Diagnostics.Invalid, "could not generate object file");
  711. RETURN FALSE
  712. END;
  713. IF TraceLinking THEN
  714. D.String("assembling instance done. "); D.Ln;
  715. END;
  716. NEW (code, 0); NEW (data, 0);
  717. COPY(instanceName, msg); Strings.Append(msg,".log"); logFile := Files.New(msg);
  718. IF logFile # NIL THEN NEW(linkerLog,logFile,0) ELSE logFile := NIL END;
  719. NEW (linker, specification.diagnostics, linkerLog, GenericLinker.UseInitCode, code, data);
  720. linker.SetLinkRoot("" (* linkRoot *)); (* take all initcode sections *)
  721. StaticLinker.ReadObjectFile(instanceName, "",objectFileExtension,linker);
  722. (* do linking after having read in all blocks to account for potential constraints *)
  723. IF ~linker.error THEN linker.Link; END;
  724. system := backend.GetSystem();
  725. instructionMemorySize := instance.instructionMemorySize;
  726. dataMemorySize := instance.dataMemorySize;
  727. IF instructionMemorySize = 0 THEN
  728. instructionMemorySize := type.instructionMemorySize
  729. END;
  730. IF dataMemorySize = 0 THEN
  731. dataMemorySize := type.dataMemorySize
  732. END;
  733. IF (instructionMemorySize > 0) & (instructionMemorySize < code.SizeInBits() DIV system.codeUnit) THEN
  734. diagnostics.Error(instanceName,Diagnostics.Invalid, Diagnostics.Invalid, "specified instruction memory size too small");
  735. error := TRUE;
  736. ELSIF instructionMemorySize = 0 THEN
  737. instructionMemorySize := code.SizeInBits() DIV system.codeUnit;
  738. END;
  739. dataMemorySize := MAX(data.SizeInBits() DIV system.dataUnit, dataMemorySize);
  740. instance.SetInstructionMemorySize(instructionMemorySize);
  741. instance.SetDataMemorySize(dataMemorySize);
  742. IF (dataMemorySize - data.SizeInBits() DIV system.dataUnit) < MinimalStackSize THEN
  743. diagnostics.Error(specification.name,Diagnostics.Invalid, Diagnostics.Invalid, "specified data memory size too small");
  744. error := TRUE;
  745. END;
  746. Files.JoinExtension(instanceName,ActiveCells.CodeFileExtension,codeFileName);
  747. Files.JoinExtension(instanceName,ActiveCells.DataFileExtension,dataFileName);
  748. IF ~linker.error THEN
  749. StaticLinker.WriteOutputFile (code, codeFileName, linker, StaticLinker.WriteTRMCodeFile);
  750. StaticLinker.WriteOutputFile (data, dataFileName, linker, StaticLinker.WriteTRMDataFile);
  751. IF linkerLog # NIL THEN linkerLog.Update; Files.Register(logFile) END;
  752. IF specification.log # NIL THEN
  753. specification.log.String(instanceName);
  754. specification.log.String(" linked. IM = ");specification.log.Int(instructionMemorySize,1);
  755. specification.log.String(" (used: "); specification.log.Int(code.SizeInBits() DIV system.codeUnit,1);
  756. specification.log.String("), DM = "); specification.log.Int(dataMemorySize,1);
  757. specification.log.String(" (used: "); specification.log.Int(data.SizeInBits() DIV system.dataUnit,1);
  758. specification.log.String(")");
  759. specification.log.Ln; specification.log.Update;
  760. specification.log.String("generated code file: ");specification.log.String(codeFileName); specification.log.Ln;
  761. specification.log.String("generated data file: ");specification.log.String(dataFileName); specification.log.Ln;
  762. END;
  763. ELSE
  764. msg := "could not link ";
  765. Strings.Append(msg,linkRoot);
  766. diagnostics.Error("",Diagnostics.Invalid, Diagnostics.Invalid, msg);
  767. END;
  768. RETURN ~linker.error & ~error
  769. END LinkInstance;
  770. END LinkerObject;
  771. VAR obj: LinkerObject; spec: ActiveCells.Specification;
  772. BEGIN
  773. spec := ActiveCells.Clone(activeCellsSpecification)(ActiveCells.Specification);
  774. ActiveCells.FlattenNetwork(spec);
  775. NEW(obj,spec,backend,objectFileFormat);
  776. IF obj.error THEN RETURN FALSE END;
  777. RETURN spec.ForEachInstanceDo(obj.LinkInstance);
  778. END LinkActiveCells;
  779. PROCEDURE Link*(context: Commands.Context);
  780. VAR
  781. input: Streams.Reader;
  782. diagnostics: Diagnostics.StreamDiagnostics;
  783. defaultBackend: Backend.Backend;
  784. objectFileFormat: Formats.ObjectFileFormat;
  785. filename, name, targetFile: Files.FileName;
  786. assemblinker: Linker;
  787. error, result, parsed: BOOLEAN;
  788. options:Options.Options;
  789. position: LONGINT;
  790. moduleName: SyntaxTree.IdentifierString;
  791. PROCEDURE Error(CONST error: ARRAY OF CHAR);
  792. BEGIN
  793. IF diagnostics # NIL THEN
  794. diagnostics.Error("",Diagnostics.Invalid,Diagnostics.Invalid,error);
  795. END;
  796. END Error;
  797. BEGIN
  798. input := context.arg;
  799. NEW(diagnostics, context.out);
  800. result := TRUE;
  801. NEW(options);
  802. options.Add("b","backend",Options.String);
  803. options.Add(0X, "objectFile", Options.String);
  804. options.Add(0X, "targetFile", Options.String);
  805. position := input.Pos();
  806. parsed := options.Parse(input,NIL);
  807. IF options.GetString("b", name) THEN
  808. IF name = "" THEN defaultBackend := NIL
  809. ELSE
  810. defaultBackend := Backend.GetBackendByName(name);
  811. IF (defaultBackend = NIL) THEN
  812. Error("backend could not be installed"); result := FALSE;
  813. END;
  814. END;
  815. ELSE defaultBackend := Backend.GetBackendByName(DefaultBackend);
  816. IF defaultBackend = NIL THEN Error("default backend could not be installed"); result := FALSE END;
  817. END;
  818. IF options.GetString("objectFile",name) THEN
  819. IF name = "" THEN objectFileFormat := NIL
  820. ELSE
  821. objectFileFormat := Formats.GetObjectFileFormat(name);
  822. IF objectFileFormat = NIL THEN Error("object file format could not be installed"); result := FALSE END;
  823. END;
  824. ELSIF defaultBackend # NIL THEN
  825. objectFileFormat := defaultBackend.DefaultObjectFileFormat();
  826. END;
  827. IF defaultBackend # NIL THEN defaultBackend.DefineOptions (options); END;
  828. IF objectFileFormat # NIL THEN objectFileFormat.DefineOptions(options); END;
  829. IF result & ~parsed THEN
  830. options.Clear;
  831. input.SetPos(position);
  832. result := options.Parse(input,context.error)
  833. END;
  834. IF result THEN
  835. IF defaultBackend # NIL THEN defaultBackend.GetOptions (options) END;
  836. IF objectFileFormat # NIL THEN objectFileFormat.GetOptions(options) END;
  837. IF ~options.GetString("targetFile",targetFile) THEN targetFile := "" END;
  838. END;
  839. error := ~result;
  840. IF targetFile # "" THEN
  841. NEW(assemblinker, diagnostics, defaultBackend, "");
  842. END;
  843. WHILE Basic.GetStringParameter(input,filename) & ~error DO
  844. IF targetFile = "" THEN NEW(assemblinker, diagnostics, defaultBackend, "") END;
  845. IF assemblinker.LoadModule(filename, FALSE) THEN
  846. assemblinker.MarkReachabilityOfAll(TRUE);
  847. FileNameToModuleName(filename, moduleName);
  848. IF (targetFile = "") & assemblinker.GenerateObjectFile(objectFileFormat, context.out, moduleName) THEN
  849. diagnostics.Information(filename, Diagnostics.Invalid, Diagnostics.Invalid, "done.")
  850. ELSIF targetFile # "" THEN
  851. diagnostics.Information(filename, Diagnostics.Invalid, Diagnostics.Invalid, "loaded.")
  852. ELSE
  853. error := TRUE
  854. END
  855. ELSE
  856. error := TRUE
  857. END
  858. END;
  859. IF ~error & (targetFile # "") THEN
  860. assemblinker.PrearrangeReachableDataSections;
  861. IF assemblinker.GenerateObjectFile(objectFileFormat, context.out, targetFile)
  862. THEN
  863. diagnostics.Information(targetFile, Diagnostics.Invalid, Diagnostics.Invalid, "generated.")
  864. ELSE error := FALSE
  865. END;
  866. END;
  867. END Link;
  868. END FoxIntermediateLinker.