OpenASIP 2.2
Loading...
Searching...
No Matches
InlineAsmParser.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2002-2017 Tampere University.
3 *
4 * This file is part of TTA-Based Codesign Environment (TCE).
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23*/
24/**
25 * @file InlineAsmParser.cc
26 *
27 * Implementation of inline assembly parser.
28 *
29 * @author Henry Linjamäki (henry.linjamaki-no.spam-tut.fi)
30 * @note rating: red
31 */
32
33#include "InlineAsmParser.hh"
34
35#include <memory>
36#include <regex>
37#include <algorithm>
38
39#include "AssemblerParser.hh"
40
41#include "Machine.hh"
42#include "Binary.hh"
43#include "BasicBlock.hh"
44#include "Program.hh"
47#include "Procedure.hh"
48#include "Instruction.hh"
49#include "Move.hh"
50#include "Terminal.hh"
51#include "TerminalImmediate.hh"
53#include "Immediate.hh"
54#include "TPEFProgramFactory.hh"
55#include "LiveRangeData.hh"
56
57#include "TCETargetMachine.hh"
58#include "LLVMUtilities.hh"
59
60#include <llvm/CodeGen/MachineOperand.h>
61#include <llvm/CodeGen/MachineInstr.h>
62#include "llvm/IR/InlineAsm.h"
63#include "llvm/IR/Mangler.h"
64#include <llvm/ADT/SmallString.h>
65
66using namespace llvm;
67
69 const llvm::TCETargetMachine& tm,
70 const llvm::Mangler& mangler) :
71 tm_(tm), mangler_(mangler) {
72 }
73
74/**
75 * Parses the given inline assembly instruction to POM basic block.
76 *
77 * After call parser diagnostic object is populated with encountered warning
78 * and error reports if any. The object is received via diagnostics()
79 * function. The object is cleared next time this method is called.
80 *
81 * @param inlineAsmMI The Instruction holding an inline asm block.
82 * @param bb The basic block to add parsed code into.
83 * @param irm The manager in where instruction references are copied to.
84 * @return False if any errors in parsing. Otherwise returns true;
85 */
86bool
88 const llvm::MachineInstr& inlineAsmMI,
89 const std::map<std::string, unsigned>& symbolTable,
92
93#ifdef DEBUG_INLINE_ASM_PARSER
94 std::cerr << "*** before position string substitution:" << std::endl;
95 inlineAsmMI.print(llvm::dbgs());
96#endif
97
98 std::string asmString = substituteAsmString(
99 inlineAsmMI, symbolTable, mangler_);
100
101#ifdef DEBUG_INLINE_ASM_PARSER
102 std::cerr << "*** before inline asm parsing:" << std::endl
103 << asmString << std::endl; //DEBUG
104#endif
105
106 // Parse inline asm.
107 parserDiagnostic_.reset(std::make_shared<std::string>(asmString));
108 std::unique_ptr<TPEF::Binary> bin(new TPEF::Binary());
109 const TTAMachine::Machine& mach = tm_.ttaMachine();
110 AssemblerParser parser(
111 *bin, mach, &parserDiagnostic_, /* codeLinesOnly = */ true);
112
113 try {
114 if (!parser.compile(asmString)) {
116 parser.errorLine(), "Syntax error in inline assembly.");
117 return false;
118 }
119 parser.finalize(mach.isLittleEndian());
120 } catch (CompileError& e) {
121 reportError(parser.errorLine(), e.errorMessage());
122 return false;
123 } catch (Exception& e) {
124 reportError(parser.errorLine(), e.errorMessage());
125 return false;
126 }
127
128 // Convert to POM.
129 std::unique_ptr<TTAProgram::Program> prog;
130 try {
131 // TODO Add universal machine for sequential code.
132 TTAProgram::TPEFProgramFactory tpefFactory(*bin, mach);
133 prog.reset(tpefFactory.build());
134 } catch (Exception& e) {
136 return false;
137 }
138
139 assert(prog);
140
141 copyInstructions(*prog, bb, irm);
142 addDebugInfoToInlineAsmBB(inlineAsmMI, bb);
143 addLiveRangeData(inlineAsmMI, bb);
144
145 if (!sanityChecks(inlineAsmMI, bb)) return false;
146
147#ifdef DEBUG_INLINE_ASM_PARSER
148 std::cerr << "*** After inline asm parsing:" << std::endl
149 << bb.toString() << std::endl;
150#endif
151
152 return true;
153}
154
155/**
156 * Adds gebug info to moves of the parsed inline asm.
157 */
158void
160 const MachineInstr& mi,
162
163 // TODO/FIXME For some reason the source locations for asm block are
164 // shifted, pointing to incorrect lines of the source files.
165 // This error(?) can be seen already in disll.
166
167 std::string sourceFileName;
168 size_t sourceLineNumber;
169 std::tie(sourceFileName, sourceLineNumber) = getSourceLocationInfo(mi);
170
171 if (sourceFileName.size() >
173 sourceFileName =
174 sourceFileName.substr(
175 sourceFileName.size() -
178 }
181 sourceLineNumber);
182 if (sourceFileName.empty()) sourceFileName = "???";
185 sourceFileName);
186
187 for (int i = 0; i < bb.instructionCount(); i++) {
189 for (int m = 0; m < instr.moveCount(); m++) {
190 TTAProgram::Move& move = instr.move(m);
191 move.addAnnotation(srcLineAnn);
192 move.addAnnotation(srcFileAnn);
193 }
194 }
195}
196
197/**
198 * Fills live range data structure.
199 *
200 * This method only fills inlineAsmX_ fields in the structure.
201 * Data dependency graph builder handles the fields later on and build the
202 * proper live range data.
203 */
204void
206 const llvm::MachineInstr& mi,
208
209 if (!bb.liveRangeData_) {
210 bb.liveRangeData_ = new LiveRangeData();
211 }
212
213 auto& liveRangeData = *bb.liveRangeData_;
214 AsmOperandMap asmOperandMap = getInlineAsmOperands(mi);
215 for (auto& opds : asmOperandMap) {
216 auto asmOpdKind = std::get<0>(opds.second);
217 auto& asmOpdNodes = std::get<1>(opds.second);
218 TCEString reg;
219 switch (asmOpdKind) {
220 default:
221 break;
222 case InlineAsm::Kind_RegUse:
223 assert(asmOpdNodes.size() == 1);
224 reg = registerName(*asmOpdNodes.at(0));
225 liveRangeData.inlineAsmRegUses_.insert(reg);
226 break;
227 case InlineAsm::Kind_RegDefEarlyClobber:
228 case InlineAsm::Kind_RegDef:
229 assert(asmOpdNodes.size() == 1);
230 reg = registerName(*asmOpdNodes.at(0));
231 liveRangeData.inlineAsmRegDefs_.insert(reg);
232 break;
233 case InlineAsm::Kind_Clobber:
234 assert(asmOpdNodes.size() == 1);
235 reg = registerName(*asmOpdNodes.at(0));
236 liveRangeData.inlineAsmClobbers_.insert(reg);
237 break;
238 }
239 }
240}
241
242/**
243 * Performs sanity checks of parsed inline asm snippet.
244 *
245 * The method reports issues to the diagnostic object.
246 *
247 * @returns True if no errors were encountered beside some warnings.
248 * Otherwise, returns false on any error.
249 */
250bool
252 const llvm::MachineInstr&,
253 TTAProgram::BasicBlock&) const {
254
255 // Check unintended input register overwrite. //
256 // TODO
257
258 // Checks no input operands are clobbered. //
259 // TODO
260
261 return true;
262}
263
264/**
265 * Returns register name of the machine operand (of reg type).
266 */
267std::string
268InlineAsmParser::registerName(const llvm::MachineOperand& mo) const {
269 assert(mo.isReg());
270 return tm_.registerName(mo.getReg());
271}
272
273/**
274 * Returns substituted assembly string.
275 *
276 * @param mi The instruction holding the inline asm.
277 * @param symbolTable Table to traslate mangled names to addresses.
278 * @param mangler The name mangler.
279 */
280std::string
282 const llvm::MachineInstr& mi,
283 const std::map<std::string, unsigned>& symbolTable,
284 const llvm::Mangler& mangler) {
285
286
287 assert(mi.isInlineAsm() && "MI must hold inline asm.");
288 std::string result = mi.getOperand(
289 InlineAsm::MIOp_AsmString).getSymbolName();
290 assert(!result.empty());
291
292 // If TCE operation macro - the _TCE_<operation> kinds.
293 if (!InlineAsmParser::isInlineAsm(mi)) return result;
294
295 auto mangledNameFn = [&mangler](const GlobalValue* gv) -> std::string {
296 SmallString<256> Buffer;
297 mangler.getNameWithPrefix(Buffer, gv, false);
298 std::string name(Buffer.c_str());
299
300 return name;
301 };
302
303 // Maps template strings to the assigned operand. e.g "$0" => "RF.5".
304 std::map<std::string, std::string> templateStringMap;
305
306 const std::string tmplPrefix = "$";
307 std::string unhandledKind;
309 for (auto& asmOpd : opdMap) {
310 unsigned asmOpdPos = asmOpd.first;
311 unsigned asmOpdKind = std::get<0>(asmOpd.second);
312 std::vector<const llvm::MachineOperand*>& flagOpds =
313 std::get<1>(asmOpd.second);
314 unsigned numAsmOpds = flagOpds.size();
315 switch (asmOpdKind) {
316 case InlineAsm::Kind_Mem:
317 if (unhandledKind.empty()) unhandledKind = "mem";
318 // fall-through
319 default:
320 if (unhandledKind.empty()) unhandledKind = "??";
322 std::cerr << "substituteAsmString():"
323 << " Ignoring asm operand kind: "
324 << unhandledKind << std::endl;
325 }
326 unhandledKind.clear();
327 break;
328
329 case InlineAsm::Kind_RegDefEarlyClobber:
330 case InlineAsm::Kind_RegDef:
331 case InlineAsm::Kind_RegUse:
332 assert(numAsmOpds == 1);
333 templateStringMap.insert(std::make_pair(
334 tmplPrefix + std::to_string(asmOpdPos),
335 tm_.registerName(flagOpds.at(0)->getReg())));
336 break;
337
338 case InlineAsm::Kind_Imm:
339 assert(numAsmOpds == 1);
340 if (flagOpds.at(0)->isImm()) {
341 templateStringMap.insert(std::make_pair(
342 tmplPrefix + std::to_string(asmOpdPos),
343 std::to_string(flagOpds.at(0)->getImm())));
344 } else if (flagOpds.at(0)->isGlobal()) {
345 std::string name = mangledNameFn(flagOpds.at(0)->getGlobal());
346 unsigned address = 0;
347 if (symbolTable.find(name) != symbolTable.end()) {
348 address = symbolTable.at(name)
349 + flagOpds.at(0)->getOffset();
350 // TODO: could be address to function. Can not handle that
351 // yet.
352 } else {
355 "Could not determine address of symbol '"
356 + name + "'. \nNote: Functions as inline asm "
357 "operands are not supported.");
358 }
359
360 templateStringMap.insert(std::make_pair(
361 tmplPrefix + std::to_string(asmOpdPos),
362 std::to_string(address)));
363 } else {
364 assert(false);
365 }
366 break;
367 case InlineAsm::Kind_Clobber:
368 break; // No need to handle.
369 }
370 }
371
372 // Searches for "$<num>" string. Returns tuple of (position, length)
373 // if found. Returns (string::npos, 0) if not found.
374 auto findTemplateStrFn = [](const std::string& str, size_t pos)
375 -> std::tuple<size_t, size_t> {
376 if (pos > str.size()) {
377 return std::make_tuple(std::string::npos, 0);
378 }
379 size_t len = 0;
380 while ((pos = str.find("$", pos)) != std::string::npos) {
381 size_t endPos = str.find_first_not_of("0123456789", pos+1);
382 if (endPos == std::string::npos) {
383 // Check template string at end of string, e.g '$4<EOF>'
384 if (str.size()-pos > 1) {
385 return std::make_tuple(pos, str.size()-pos);
386 }
387 pos = std::string::npos;
388 break;
389 }
390 len = endPos-pos;
391 if (len > 1) break; // Ignores "$$" and "${:uid}" strings.
392 pos += len;
393 }
394 return std::make_tuple(pos, len);
395 };
396
397 size_t pos = 0;
398 size_t len = 0;
399 std::set<std::string> replacedTemplStrs;
400 while (true) {
401 std::tie(pos, len) = findTemplateStrFn(result, pos);
402 if (pos == std::string::npos) break;
403 const std::string& templStr = result.substr(pos, len);
404 replacedTemplStrs.insert(templStr);
405 result.replace(pos, len, templateStringMap.at(templStr));
406 pos += len;
407 }
408
409 // Unreferenced template string, especially for output operands, may break
410 // semantics of a program code.
411 for (auto& tmplStrPair : templateStringMap) {
412 const std::string& tmplStr = tmplStrPair.first;
413 if (replacedTemplStrs.count(tmplStr)) continue;
414 std::string msg;
415 try {
416 auto opdIdx = Conversion::toInt(tmplStr.substr(1))+1;
417 msg = std::to_string(opdIdx) + ". operand is unreferenced";
418 } catch (NumberFormatException&) {
419 msg = "There is unreferenced operand";
420 }
421 std::string srcFile;
422 size_t srcLine;
423 std::tie(srcFile, srcLine) = getSourceLocationInfo(mi);
424 std::string srcLoc;
425 if (!srcFile.empty()) {
426 srcLoc = srcFile + ":" + std::to_string(srcLine) + ": ";
427 }
428 std::cerr << srcLoc << "Warning: " << msg
429 << " in an inline asm block."
430 << std::endl;
431 }
432
433 // Replace "%=" template strings (redubbed as "${:uid}" in LLVM).
434 std::string uidTmplStr = "${:uid}";
435 auto uid = std::to_string(asmId_++);
436 pos = 0;
437 while ((pos = result.find(uidTmplStr, pos)) != std::string::npos) {
438 result.replace(pos, uidTmplStr.size(), uid);
439 pos += uid.size();
440 }
441 assert(!result.empty());
442 return result;
443}
444
445/**
446 * Returns true if the instruction represents an inline asm block recognized
447 * by this parser.
448 */
449bool
450InlineAsmParser::isInlineAsm(const llvm::MachineInstr& mi) {
451 if (mi.isInlineAsm()) {
452 std::string amsStr(
453 mi.getOperand(InlineAsm::MIOp_AsmString).getSymbolName());
454 return amsStr.find_first_of("->;") != std::string::npos;
455 }
456 return false;
457}
458
459/*
460 * Report an error at inline assembly string line.
461 */
462void
463InlineAsmParser::reportError(size_t lineNum, const std::string& errorMsg) {
464 parserDiagnostic_.addError(lineNum, errorMsg);
465}
466
467void
468InlineAsmParser::reportError(const std::string& errorMsg) {
469 parserDiagnostic_.addError(errorMsg);
470}
471
472std::vector<TTAProgram::TerminalInstructionReference*>
474
476
477 std::vector<TTAProgram::TerminalInstructionReference*> result;
478
479 for (int i = 0; i < instr.moveCount(); i++) {
480 auto& move = instr.move(i);
481 auto& srcTerml = move.source();
482 if (srcTerml.isInstructionAddress()) {
483 assert(dynamic_cast<TerminalInstructionReference*>(&srcTerml));
484 result.push_back(static_cast<TerminalInstructionReference*>(
485 &srcTerml));
486 }
487 }
488
489 for (int i = 0; i < instr.immediateCount(); i++) {
490 auto& valTerml = instr.immediate(i).value();
491 if (valTerml.isInstructionAddress()) {
492 assert(dynamic_cast<TerminalInstructionReference*>(&valTerml));
493 result.push_back(static_cast<TerminalInstructionReference*>(
494 &valTerml));
495 }
496 }
497
498 return result;
499}
500
501void
504 TTAProgram::BasicBlock& targetBB,
506
507 using namespace TTAProgram;
508
509 auto procCount = from.procedureCount();
510 // There should be only one default procedure from parsed inline asm.
511 // However, file-level inline asm could have more: TODO when supported.
512 assert(from.procedureCount() == 1
513 && "Procedures are not allowed in non-file-level inline asm.");
514
515 // A map for fixing instruction references. Old instr -> copied instr.
516 std::map<const TTAProgram::Instruction*, TTAProgram::Instruction*>
517 instrCopyMap;
518 for (decltype(procCount) i = 0; i < procCount; i++) {
519 const auto& proc = from.procedureAtIndex(i);
520 auto instrCount = proc.instructionCount();
521 for (decltype(instrCount) i = 0; i < instrCount; i++) {
522 const auto& oldInstr = proc.instructionAtIndex(i);
523 auto newInstr = oldInstr.copy();
524 targetBB.add(newInstr);
525 instrCopyMap.insert({&oldInstr, newInstr});
526 }
527 }
528
529 for (auto& pair : instrCopyMap) {
530 auto& newInstr = *pair.second;
531 for (auto& refTerm : getInstructionReferenceTerminals(newInstr)) {
532 auto& oldRefInstr = refTerm->instructionReference().instruction();
533 assert(instrCopyMap.count(&oldRefInstr));
534 auto newRefInstr = instrCopyMap.at(&oldRefInstr);
535 auto newRef = irm.createReference(*newRefInstr);
536 refTerm->setInstructionReference(newRef);
537 }
538 }
539}
540
541
#define assert(condition)
#define THROW_EXCEPTION(exceptionType, message)
Exception wrapper macro that automatically includes file name, line number and function name where th...
Definition Exception.hh:39
std::vector< TTAProgram::TerminalInstructionReference * > getInstructionReferenceTerminals(TTAProgram::Instruction &instr)
AsmOperandMap getInlineAsmOperands(const llvm::MachineInstr &mi)
POP_COMPILER_DIAGS std::tuple< std::string, size_t > getSourceLocationInfo(const llvm::MachineInstr &mi)
std::map< AsmPosition, AsmOperands > AsmOperandMap
static bool spamVerbose()
void reset(std::shared_ptr< const std::string > assemblyText)
void addError(UValue lineNumber, const std::string &message)
static int toInt(const T &source)
std::string errorMessage() const
Definition Exception.cc:123
static bool isInlineAsm(const llvm::MachineInstr &mi)
const llvm::Mangler & mangler_
The symbol name mangler for MIs' symbolic references.
std::string substituteAsmString(const llvm::MachineInstr &mi, const std::map< std::string, unsigned > &symbolTable, const llvm::Mangler &mangler)
AssemblyParserDiagnostic parserDiagnostic_
The diagnostic object to report parse and compile warnings and errors to.
std::string registerName(const llvm::MachineOperand &mo) const
static void addDebugInfoToInlineAsmBB(const llvm::MachineInstr &mi, TTAProgram::BasicBlock &bb)
InlineAsmParser()=delete
static void copyInstructions(TTAProgram::Program &prog, TTAProgram::BasicBlock &targetBB, TTAProgram::InstructionReferenceManager &irm)
bool sanityChecks(const llvm::MachineInstr &mi, TTAProgram::BasicBlock &bb) const
bool parse(const llvm::MachineInstr &inlineAsmMI, const std::map< std::string, unsigned > &symbolTable, TTAProgram::BasicBlock &bb, TTAProgram::InstructionReferenceManager &irm)
const llvm::TCETargetMachine & tm_
The target machine parsing context.
unsigned asmId_
The unique id for "%=" template strings. Each parse() call increases the count.
void reportError(size_t lineNum, const std::string &errorMsg)
void addLiveRangeData(const llvm::MachineInstr &mi, TTAProgram::BasicBlock &bb)
static const size_t MAX_ANNOTATION_BYTES
Maximum number of bytes that annotation may contain.
bool isLittleEndian() const
Definition Machine.hh:258
void addAnnotation(const ProgramAnnotation &annotation)
LiveRangeData * liveRangeData_
virtual std::string toString() const
virtual void add(Instruction *ins)
virtual int instructionCount() const
virtual Instruction & instructionAtIndex(int index) const
TerminalImmediate & value() const
Definition Immediate.cc:103
InstructionReference createReference(Instruction &ins)
Move & move(int i) const
Immediate & immediate(int i) const
Terminal & source() const
Definition Move.cc:302
@ ANN_DEBUG_SOURCE_CODE_LINE
The line number in the source code file the annotated move originates from.
@ ANN_DEBUG_SOURCE_CODE_PATH
debugging info annotations
const Procedure & procedureAtIndex(int index) const
Definition Program.cc:508
int procedureCount() const
Definition Program.cc:610
virtual const TTAMachine::Machine & ttaMachine() const
std::string registerName(unsigned dwarfRegNum) const
void finalize(bool littleEndian) const
bool compile(const std::string &asmCode) const