2 * Inline assembler for the GCC D compiler.
4 * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d)
8 * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d
14 import core
.stdc
.string
;
16 import dmd
.arraytypes
;
17 import dmd
.astcodegen
;
20 import dmd
.expression
;
21 import dmd
.expressionsem
;
22 import dmd
.identifier
;
27 import dmd
.statementsem
;
31 /***********************************
32 * Parse list of extended asm input or output operands.
35 * | SymbolicName(opt) StringLiteral ( AssignExpression )
36 * | SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
42 * s = asm statement to parse
44 * number of operands added to the gcc asm statement
46 int parseExtAsmOperands(Parser
)(Parser p
, GccAsmStatement s
)
54 Expression constraint
;
56 switch (p
.token
.value
)
64 if (p
.peekNext() == TOK
.identifier
)
66 // Skip over opening `[`
68 // Store the symbolic name
74 p
.error(s
.loc
, "expected identifier after `[`");
77 // Look for closing `]`
78 p
.check(TOK
.rightBracket
);
79 // Look for the string literal and fall through
80 if (p
.token
.value
== TOK
.string_
)
86 constraint
= p
.parsePrimaryExp();
88 // Old parser allowed omitting parentheses around the expression.
89 // Deprecated in 2.091. Can be made permanent error after 2.100
90 if (p
.token
.value
!= TOK
.leftParenthesis
)
92 arg
= p
.parseAssignExp();
93 deprecation(arg
.loc
, "`%s` must be surrounded by parentheses", arg
.toChars());
97 // Look for the opening `(`
98 p
.check(TOK
.leftParenthesis
);
99 // Parse the assign expression
100 arg
= p
.parseAssignExp();
101 // Look for the closing `)`
102 p
.check(TOK
.rightParenthesis
);
107 s
.names
= new Identifiers();
108 s
.constraints
= new Expressions();
109 s
.args
= new Expressions();
113 s
.constraints
.push(constraint
);
116 if (p
.token
.value
== TOK
.comma
)
121 p
.error("expected constant string constraint for operand, not `%s`",
127 while (p
.token
.value
!= TOK
.rightCurly
&&
128 p
.token
.value
!= TOK
.semicolon
&&
129 p
.token
.value
!= TOK
.endOfFile
)
135 /***********************************
136 * Parse list of extended asm clobbers.
140 * | StringLiteral , Clobbers
144 * array of parsed clobber expressions
146 Expressions
*parseExtAsmClobbers(Parser
)(Parser p
)
148 Expressions
*clobbers
;
154 switch (p
.token
.value
)
162 clobber
= p
.parsePrimaryExp();
164 clobbers
= new Expressions();
165 clobbers
.push(clobber
);
167 if (p
.token
.value
== TOK
.comma
)
172 p
.error("expected constant string constraint for clobber name, not `%s`",
178 while (p
.token
.value
!= TOK
.rightCurly
&&
179 p
.token
.value
!= TOK
.semicolon
&&
180 p
.token
.value
!= TOK
.endOfFile
)
186 /***********************************
187 * Parse list of extended asm goto labels.
191 * | Identifier , GotoLabels
195 * array of parsed goto labels
197 Identifiers
*parseExtAsmGotoLabels(Parser
)(Parser p
)
203 switch (p
.token
.value
)
211 labels
= new Identifiers();
212 labels
.push(p
.token
.ident
);
214 if (p
.nextToken() == TOK
.comma
)
219 p
.error("expected identifier for goto label name, not `%s`",
225 while (p
.token
.value
!= TOK
.rightCurly
&&
226 p
.token
.value
!= TOK
.semicolon
&&
227 p
.token
.value
!= TOK
.endOfFile
)
233 /***********************************
234 * Parse a gcc asm statement.
235 * There are three forms of inline asm statements, basic, extended, and goto.
238 * | BasicAsmInstruction
239 * | ExtAsmInstruction
240 * | GotoAsmInstruction
242 * | BasicAsmInstruction:
245 * | ExtAsmInstruction:
246 * | AssignExpression : Operands(opt) : Operands(opt) : Clobbers(opt)
248 * | GotoAsmInstruction:
249 * | AssignExpression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
252 * s = asm statement to parse
254 * the parsed gcc asm statement
256 GccAsmStatement
parseGccAsm(Parser
)(Parser p
, GccAsmStatement s
)
258 s
.insn
= p
.parseAssignExp();
259 if (p
.token
.value
== TOK
.semicolon || p
.token
.value
== TOK
.endOfFile
)
262 // No semicolon followed after instruction template, treat as extended asm.
263 foreach (section
; 0 .. 4)
267 final switch (section
)
270 s
.outputargs
= p
.parseExtAsmOperands(s
);
274 p
.parseExtAsmOperands(s
);
278 s
.clobbers
= p
.parseExtAsmClobbers();
282 s
.labels
= p
.parseExtAsmGotoLabels();
286 if (p
.token
.value
== TOK
.semicolon || p
.token
.value
== TOK
.endOfFile
)
290 p
.check(TOK
.semicolon
);
295 /***********************************
296 * Parse and run semantic analysis on a GccAsmStatement.
298 * s = gcc asm statement being parsed
299 * sc = the scope where the asm statement is located
301 * the completed gcc asm statement, or null if errors occurred
303 extern (C
++) public Statement
gccAsmSemantic(GccAsmStatement s
, Scope
*sc
)
305 //printf("GccAsmStatement.semantic()\n");
306 scope p
= new Parser
!ASTCodegen(sc
._module
, ";", false);
308 // Make a safe copy of the token list before parsing.
309 Token
*toklist
= null;
310 Token
**ptoklist
= &toklist
;
312 for (Token
*token
= s
.tokens
; token
; token
= token
.next
)
314 *ptoklist
= p
.allocateToken();
315 memcpy(*ptoklist
, token
, Token
.sizeof
);
316 ptoklist
= &(*ptoklist
).next
;
322 // Parse the gcc asm statement.
323 const errors
= global
.errors
;
324 s
= p
.parseGccAsm(s
);
325 if (errors
!= global
.errors
)
329 // Fold the instruction template string.
330 s
.insn
= semanticString(sc
, s
.insn
, "asm instruction template");
332 if (s
.labels
&& s
.outputargs
)
333 s
.error("extended asm statements with labels cannot have output constraints");
335 // Analyse all input and output operands.
338 foreach (i
; 0 .. s
.args
.dim
)
340 Expression e
= (*s
.args
)[i
];
341 e
= e
.expressionSemantic(sc
);
342 // Check argument is a valid lvalue/rvalue.
343 if (i
< s
.outputargs
)
344 e
= e
.modifiableLvalue(sc
, null);
345 else if (e
.checkValue())
349 e
= (*s
.constraints
)[i
];
350 e
= e
.expressionSemantic(sc
);
351 assert(e
.op
== EXP
.string_
&& (cast(StringExp
) e
).sz
== 1);
352 (*s
.constraints
)[i
] = e
;
356 // Analyse all clobbers.
359 foreach (i
; 0 .. s
.clobbers
.dim
)
361 Expression e
= (*s
.clobbers
)[i
];
362 e
= e
.expressionSemantic(sc
);
363 assert(e
.op
== EXP
.string_
&& (cast(StringExp
) e
).sz
== 1);
364 (*s
.clobbers
)[i
] = e
;
368 // Analyse all goto labels.
371 foreach (i
; 0 .. s
.labels
.dim
)
373 Identifier ident
= (*s
.labels
)[i
];
374 GotoStatement gs
= new GotoStatement(s
.loc
, ident
);
376 s
.gotos
= new GotoStatements();
378 gs
.statementSemantic(sc
);
387 import dmd
.mtype
: TypeBasic
;
389 uint errors
= global
.startGagging();
390 scope(exit
) global
.endGagging(errors
);
392 // If this check fails, then Type._init() was called before reaching here,
393 // and the entire chunk of code that follows can be removed.
394 assert(ASTCodegen
.Type
.tint32
is null);
395 // Minimally initialize the cached types in ASTCodegen.Type, as they are
396 // dependencies for some fail asm tests to succeed.
397 ASTCodegen
.Type
.stringtable
._init();
400 ASTCodegen
.Type
.deinitialize();
401 ASTCodegen
.Type
.tint32
= null;
403 scope tint32
= new TypeBasic(ASTCodegen
.Tint32
);
404 ASTCodegen
.Type
.tint32
= tint32
;
406 // Imitates asmSemantic if version = IN_GCC.
407 static int semanticAsm(Token
* tokens
)
409 const errors
= global
.errors
;
410 scope gas
= new GccAsmStatement(Loc
.initial
, tokens
);
411 scope p
= new Parser
!ASTCodegen(null, ";", false);
414 return global
.errors
- errors
;
417 // Imitates parseStatement for asm statements.
418 static void parseAsm(string input
, bool expectError
)
420 // Generate tokens from input test.
421 scope p
= new Parser
!ASTCodegen(null, input
, false);
424 Token
* toklist
= null;
425 Token
** ptoklist
= &toklist
;
427 p
.check(TOK
.leftCurly
);
430 if (p
.token
.value
== TOK
.rightCurly || p
.token
.value
== TOK
.endOfFile
)
432 if (p
.token
.value
== TOK
.colonColon
)
434 *ptoklist
= p
.allocateToken();
435 memcpy(*ptoklist
, &p
.token
, Token
.sizeof
);
436 (*ptoklist
).value
= TOK
.colon
;
437 ptoklist
= &(*ptoklist
).next
;
439 *ptoklist
= p
.allocateToken();
440 memcpy(*ptoklist
, &p
.token
, Token
.sizeof
);
441 (*ptoklist
).value
= TOK
.colon
;
442 ptoklist
= &(*ptoklist
).next
;
446 *ptoklist
= p
.allocateToken();
447 memcpy(*ptoklist
, &p
.token
, Token
.sizeof
);
448 ptoklist
= &(*ptoklist
).next
;
453 p
.check(TOK
.rightCurly
);
455 auto res
= semanticAsm(toklist
);
456 // Checks for both unexpected passes and failures.
457 assert((res
== 0) != expectError
);
460 /// Assembly Tests, all should pass.
461 /// Note: Frontend is not initialized, use only strings and identifiers.
462 immutable string
[] passAsmTests
= [
463 // Basic asm statement
467 // Extended asm statement
469 : "=a" (a
), "=b" (b
), "=c" (c
), "=d" (d
)
473 // Assembly with symbolic names
474 q
{ asm { "bts %[base], %[offset]"
475 : [base
] "+rm" (*ptr
),
476 : [offset
] "Ir" (bitnum
);
479 // Assembly with clobbers
483 : "ebx", "ecx", "edx";
486 // Goto asm statement
494 // Any CTFE-able string allowed as instruction template.
495 q
{ asm { generateAsm();
498 // Likewise mixins, permissible so long as the result is a string.
499 q
{ asm { mixin(`"repne"`, `~ "scasb"`);
503 q
{ asm { "" : : : "memory"; } },
504 q
{ asm { "" :: : "memory"; } },
505 q
{ asm { "" : :: "memory"; } },
506 q
{ asm { "" ::: "memory"; } },
509 immutable string
[] failAsmTests
= [
510 // Found 'h' when expecting ';'
514 // https://issues.dlang.org/show_bug.cgi?id=20592
515 q
{ asm { "nop" : [name
] string (expr
); } },
517 // Expression expected, not ';'
521 // Expression expected, not ':'
527 // Found ',' when expecting ':'
532 foreach (test; passAsmTests
)
533 parseAsm(test, false);
535 foreach (test; failAsmTests
)
536 parseAsm(test, true);