d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / iasmgcc.d
blobcd3011731f55220ceb535f8a4ff871268d80bb1b
1 /**
2 * Inline assembler for the GCC D compiler.
4 * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
5 * Authors: Iain Buclaw
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
12 module dmd.iasmgcc;
14 import core.stdc.string;
16 import dmd.arraytypes;
17 import dmd.astcodegen;
18 import dmd.dscope;
19 import dmd.errors;
20 import dmd.expression;
21 import dmd.expressionsem;
22 import dmd.identifier;
23 import dmd.globals;
24 import dmd.parse;
25 import dmd.tokens;
26 import dmd.statement;
27 import dmd.statementsem;
29 private:
31 /***********************************
32 * Parse list of extended asm input or output operands.
33 * Grammar:
34 * | Operands:
35 * | SymbolicName(opt) StringLiteral ( AssignExpression )
36 * | SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
37 * |
38 * | SymbolicName:
39 * | [ Identifier ]
40 * Params:
41 * p = parser state
42 * s = asm statement to parse
43 * Returns:
44 * number of operands added to the gcc asm statement
46 int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
48 int numargs = 0;
50 while (1)
52 Expression arg;
53 Identifier name;
54 Expression constraint;
56 switch (p.token.value)
58 case TOK.semicolon:
59 case TOK.colon:
60 case TOK.endOfFile:
61 return numargs;
63 case TOK.leftBracket:
64 if (p.peekNext() == TOK.identifier)
66 // Skip over opening `[`
67 p.nextToken();
68 // Store the symbolic name
69 name = p.token.ident;
70 p.nextToken();
72 else
74 p.error(s.loc, "expected identifier after `[`");
75 goto Lerror;
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_)
81 goto case;
82 else
83 goto default;
85 case TOK.string_:
86 constraint = p.parsePrimaryExp();
87 // @@@DEPRECATED@@@
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());
95 else
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);
105 if (!s.args)
107 s.names = new Identifiers();
108 s.constraints = new Expressions();
109 s.args = new Expressions();
111 s.names.push(name);
112 s.args.push(arg);
113 s.constraints.push(constraint);
114 numargs++;
116 if (p.token.value == TOK.comma)
117 p.nextToken();
118 break;
120 default:
121 p.error("expected constant string constraint for operand, not `%s`",
122 p.token.toChars());
123 goto Lerror;
126 Lerror:
127 while (p.token.value != TOK.rightCurly &&
128 p.token.value != TOK.semicolon &&
129 p.token.value != TOK.endOfFile)
130 p.nextToken();
132 return numargs;
135 /***********************************
136 * Parse list of extended asm clobbers.
137 * Grammar:
138 * | Clobbers:
139 * | StringLiteral
140 * | StringLiteral , Clobbers
141 * Params:
142 * p = parser state
143 * Returns:
144 * array of parsed clobber expressions
146 Expressions *parseExtAsmClobbers(Parser)(Parser p)
148 Expressions *clobbers;
150 while (1)
152 Expression clobber;
154 switch (p.token.value)
156 case TOK.semicolon:
157 case TOK.colon:
158 case TOK.endOfFile:
159 return clobbers;
161 case TOK.string_:
162 clobber = p.parsePrimaryExp();
163 if (!clobbers)
164 clobbers = new Expressions();
165 clobbers.push(clobber);
167 if (p.token.value == TOK.comma)
168 p.nextToken();
169 break;
171 default:
172 p.error("expected constant string constraint for clobber name, not `%s`",
173 p.token.toChars());
174 goto Lerror;
177 Lerror:
178 while (p.token.value != TOK.rightCurly &&
179 p.token.value != TOK.semicolon &&
180 p.token.value != TOK.endOfFile)
181 p.nextToken();
183 return clobbers;
186 /***********************************
187 * Parse list of extended asm goto labels.
188 * Grammar:
189 * | GotoLabels:
190 * | Identifier
191 * | Identifier , GotoLabels
192 * Params:
193 * p = parser state
194 * Returns:
195 * array of parsed goto labels
197 Identifiers *parseExtAsmGotoLabels(Parser)(Parser p)
199 Identifiers *labels;
201 while (1)
203 switch (p.token.value)
205 case TOK.semicolon:
206 case TOK.endOfFile:
207 return labels;
209 case TOK.identifier:
210 if (!labels)
211 labels = new Identifiers();
212 labels.push(p.token.ident);
214 if (p.nextToken() == TOK.comma)
215 p.nextToken();
216 break;
218 default:
219 p.error("expected identifier for goto label name, not `%s`",
220 p.token.toChars());
221 goto Lerror;
224 Lerror:
225 while (p.token.value != TOK.rightCurly &&
226 p.token.value != TOK.semicolon &&
227 p.token.value != TOK.endOfFile)
228 p.nextToken();
230 return labels;
233 /***********************************
234 * Parse a gcc asm statement.
235 * There are three forms of inline asm statements, basic, extended, and goto.
236 * Grammar:
237 * | AsmInstruction:
238 * | BasicAsmInstruction
239 * | ExtAsmInstruction
240 * | GotoAsmInstruction
242 * | BasicAsmInstruction:
243 * | AssignExpression
245 * | ExtAsmInstruction:
246 * | AssignExpression : Operands(opt) : Operands(opt) : Clobbers(opt)
248 * | GotoAsmInstruction:
249 * | AssignExpression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
250 * Params:
251 * p = parser state
252 * s = asm statement to parse
253 * Returns:
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)
260 goto Ldone;
262 // No semicolon followed after instruction template, treat as extended asm.
263 foreach (section; 0 .. 4)
265 p.check(TOK.colon);
267 final switch (section)
269 case 0:
270 s.outputargs = p.parseExtAsmOperands(s);
271 break;
273 case 1:
274 p.parseExtAsmOperands(s);
275 break;
277 case 2:
278 s.clobbers = p.parseExtAsmClobbers();
279 break;
281 case 3:
282 s.labels = p.parseExtAsmGotoLabels();
283 break;
286 if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
287 goto Ldone;
289 Ldone:
290 p.check(TOK.semicolon);
292 return s;
295 /***********************************
296 * Parse and run semantic analysis on a GccAsmStatement.
297 * Params:
298 * s = gcc asm statement being parsed
299 * sc = the scope where the asm statement is located
300 * Returns:
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;
317 *ptoklist = null;
319 p.token = *toklist;
320 p.scanloc = s.loc;
322 // Parse the gcc asm statement.
323 const errors = global.errors;
324 s = p.parseGccAsm(s);
325 if (errors != global.errors)
326 return null;
327 s.stc = sc.stc;
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.
336 if (s.args)
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())
346 e = ErrorExp.get();
347 (*s.args)[i] = e;
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.
357 if (s.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.
369 if (s.labels)
371 foreach (i; 0 .. s.labels.dim)
373 Identifier ident = (*s.labels)[i];
374 GotoStatement gs = new GotoStatement(s.loc, ident);
375 if (!s.gotos)
376 s.gotos = new GotoStatements();
377 s.gotos.push(gs);
378 gs.statementSemantic(sc);
382 return s;
385 unittest
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();
398 scope(exit)
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);
412 p.token = *tokens;
413 p.parseGccAsm(gas);
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);
422 p.nextToken();
424 Token* toklist = null;
425 Token** ptoklist = &toklist;
426 p.check(TOK.asm_);
427 p.check(TOK.leftCurly);
428 while (1)
430 if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
431 break;
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;
444 else
446 *ptoklist = p.allocateToken();
447 memcpy(*ptoklist, &p.token, Token.sizeof);
448 ptoklist = &(*ptoklist).next;
450 *ptoklist = null;
451 p.nextToken();
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
464 q{ asm { "nop";
465 } },
467 // Extended asm statement
468 q{ asm { "cpuid"
469 : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
470 : "a" (input);
471 } },
473 // Assembly with symbolic names
474 q{ asm { "bts %[base], %[offset]"
475 : [base] "+rm" (*ptr),
476 : [offset] "Ir" (bitnum);
477 } },
479 // Assembly with clobbers
480 q{ asm { "cpuid"
481 : "=a" (a)
482 : "a" (input)
483 : "ebx", "ecx", "edx";
484 } },
486 // Goto asm statement
487 q{ asm { "jmp %l0"
491 : Ljmplabel;
492 } },
494 // Any CTFE-able string allowed as instruction template.
495 q{ asm { generateAsm();
496 } },
498 // Likewise mixins, permissible so long as the result is a string.
499 q{ asm { mixin(`"repne"`, `~ "scasb"`);
500 } },
502 // :: token tests
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 ';'
511 q{ asm { ""h;
512 } },
514 // https://issues.dlang.org/show_bug.cgi?id=20592
515 q{ asm { "nop" : [name] string (expr); } },
517 // Expression expected, not ';'
518 q{ asm { ""[;
519 } },
521 // Expression expected, not ':'
522 q{ asm { ""
524 : "g" (a ? b : : c);
525 } },
527 // Found ',' when expecting ':'
528 q{ asm { "", "";
529 } },
532 foreach (test; passAsmTests)
533 parseAsm(test, false);
535 foreach (test; failAsmTests)
536 parseAsm(test, true);