1 
2 /* Compiler implementation of the D programming language
3  * Copyright (C) 2018-2019 by The D Language Foundation, All Rights Reserved
4  * written by Iain Buclaw
5  * http://www.digitalmars.com
6  * Distributed under the Boost Software License, Version 1.0.
7  * http://www.boost.org/LICENSE_1_0.txt
8  * https://github.com/D-Programming-Language/dmd/blob/master/src/iasmgcc.c
9  */
10 
11 /* Inline assembler for the GCC D compiler.
12  */
13 
14 #include "scope.h"
15 #include "declaration.h"
16 #include "errors.h"
17 #include "parse.h"
18 #include "statement.h"
19 
20 Expression *semantic(Expression *e, Scope *sc);
21 Statement *semantic(Statement *s, Scope *sc);
22 
23 /***********************************
24  * Parse list of extended asm input or output operands.
25  * Grammar:
26  *      | Operands:
27  *      |     SymbolicName(opt) StringLiteral ( AssignExpression )
28  *      |     SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
29  *      |
30  *      | SymbolicName:
31  *      |     [ Identifier ]
32  * Params:
33  *      p = parser state
34  *      s = asm statement to parse
35  * Returns:
36  *      number of operands added to the gcc asm statement
37  */
parseExtAsmOperands(Parser * p,GccAsmStatement * s)38 static int parseExtAsmOperands(Parser *p, GccAsmStatement *s)
39 {
40     int numargs = 0;
41 
42     while (1)
43     {
44         Expression *arg = NULL;
45         Identifier *name = NULL;
46         Expression *constraint = NULL;
47 
48         switch (p->token.value)
49         {
50             case TOKsemicolon:
51             case TOKcolon:
52             case TOKeof:
53                 return numargs;
54 
55             case TOKlbracket:
56                 if (p->peekNext() == TOKidentifier)
57                 {
58                     // Skip over openings `[`
59                     p->nextToken();
60                     // Store the symbolic name
61                     name = p->token.ident;
62                     p->nextToken();
63                 }
64                 else
65                 {
66                     p->error(s->loc, "expected identifier after `[`");
67                     goto Lerror;
68                 }
69                 // Look for closing `]`
70                 p->check(TOKrbracket);
71                 // Look for the string literal and fall through
72                 if (p->token.value != TOKstring)
73                     goto Ldefault;
74                 // fall through
75 
76             case TOKstring:
77                 constraint = p->parsePrimaryExp();
78                 // @@@DEPRECATED@@@
79                 // Old parser allowed omitting parentheses around the expression.
80                 // Deprecated in 2.091. Can be made permanent error after 2.100
81                 if (p->token.value != TOKlparen)
82                 {
83                     arg = p->parseAssignExp();
84                     deprecation(arg->loc, "`%s` must be surrounded by parentheses", arg->toChars());
85                 }
86                 else
87                 {
88                     // Look for the opening `(`
89                     p->check(TOKlparen);
90                     // Parse the assign expression
91                     arg = p->parseAssignExp();
92                     // Look for the closing `)`
93                     p->check(TOKrparen);
94                 }
95 
96                 if (!s->args)
97                 {
98                     s->names = new Identifiers();
99                     s->constraints = new Expressions();
100                     s->args = new Expressions();
101                 }
102                 s->names->push(name);
103                 s->args->push(arg);
104                 s->constraints->push(constraint);
105                 numargs++;
106 
107                 if (p->token.value == TOKcomma)
108                     p->nextToken();
109                 break;
110 
111             default:
112             Ldefault:
113                 p->error("expected constant string constraint for operand, not `%s`",
114                         p->token.toChars());
115                 goto Lerror;
116         }
117     }
118 Lerror:
119     while (p->token.value != TOKrcurly &&
120            p->token.value != TOKsemicolon &&
121            p->token.value != TOKeof)
122         p->nextToken();
123 
124     return numargs;
125 }
126 
127 /***********************************
128  * Parse list of extended asm clobbers.
129  * Grammar:
130  *      | Clobbers:
131  *      |     StringLiteral
132  *      |     StringLiteral , Clobbers
133  * Params:
134  *      p = parser state
135  * Returns:
136  *      array of parsed clobber expressions
137  */
parseExtAsmClobbers(Parser * p)138 static Expressions *parseExtAsmClobbers(Parser *p)
139 {
140     Expressions *clobbers = NULL;
141 
142     while (1)
143     {
144         Expression *clobber;
145 
146         switch (p->token.value)
147         {
148             case TOKsemicolon:
149             case TOKcolon:
150             case TOKeof:
151                 return clobbers;
152 
153             case TOKstring:
154                 clobber = p->parsePrimaryExp();
155                 if (!clobbers)
156                     clobbers = new Expressions();
157                 clobbers->push(clobber);
158 
159                 if (p->token.value == TOKcomma)
160                     p->nextToken();
161                 break;
162 
163             default:
164                 p->error("expected constant string constraint for clobber name, not `%s`",
165                         p->token.toChars());
166                 goto Lerror;
167         }
168     }
169 Lerror:
170     while (p->token.value != TOKrcurly &&
171            p->token.value != TOKsemicolon &&
172            p->token.value != TOKeof)
173         p->nextToken();
174 
175     return clobbers;
176 }
177 
178 /***********************************
179  * Parse list of extended asm goto labels.
180  * Grammar:
181  *      | GotoLabels:
182  *      |     Identifier
183  *      |     Identifier , GotoLabels
184  * Params:
185  *      p = parser state
186  * Returns:
187  *      array of parsed goto labels
188  */
parseExtAsmGotoLabels(Parser * p)189 static Identifiers *parseExtAsmGotoLabels(Parser *p)
190 {
191     Identifiers *labels = NULL;
192 
193     while (1)
194     {
195         switch (p->token.value)
196         {
197             case TOKsemicolon:
198             case TOKeof:
199                 return labels;
200 
201             case TOKidentifier:
202                 if (!labels)
203                     labels = new Identifiers();
204                 labels->push(p->token.ident);
205 
206                 if (p->nextToken() == TOKcomma)
207                     p->nextToken();
208                 break;
209 
210             default:
211                 p->error("expected identifier for goto label name, not `%s`",
212                         p->token.toChars());
213                 goto Lerror;
214         }
215     }
216 Lerror:
217     while (p->token.value != TOKrcurly &&
218            p->token.value != TOKsemicolon &&
219            p->token.value != TOKeof)
220         p->nextToken();
221 
222     return labels;
223 }
224 
225 /***********************************
226  * Parse a gcc asm statement.
227  * There are three forms of inline asm statements, basic, extended, and goto.
228  * Grammar:
229  *      | AsmInstruction:
230  *      |     BasicAsmInstruction
231  *      |     ExtAsmInstruction
232  *      |     GotoAsmInstruction
233  *      |
234  *      | BasicAsmInstruction:
235  *      |     Expression
236  *      |
237  *      | ExtAsmInstruction:
238  *      |     Expression : Operands(opt) : Operands(opt) : Clobbers(opt)
239  *      |
240  *      | GotoAsmInstruction:
241  *      |     Expression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
242  * Params:
243  *      p = parser state
244  *      s = asm statement to parse
245  * Returns:
246  *      the parsed gcc asm statement
247  */
parseGccAsm(Parser * p,GccAsmStatement * s)248 static GccAsmStatement *parseGccAsm(Parser *p, GccAsmStatement *s)
249 {
250     s->insn = p->parseExpression();
251     if (p->token.value == TOKsemicolon || p->token.value == TOKeof)
252         goto Ldone;
253 
254     // No semicolon followed after instruction template, treat as extended asm.
255     for (int section = 0; section < 4; ++section)
256     {
257         p->check(TOKcolon);
258 
259         switch (section)
260         {
261             case 0:
262                 s->outputargs = parseExtAsmOperands(p, s);
263                 break;
264 
265             case 1:
266                 parseExtAsmOperands(p, s);
267                 break;
268 
269             case 2:
270                 s->clobbers = parseExtAsmClobbers(p);
271                 break;
272 
273             case 3:
274                 s->labels = parseExtAsmGotoLabels(p);
275                 break;
276 
277             default:
278                 assert(0);
279         }
280 
281         if (p->token.value == TOKsemicolon || p->token.value == TOKeof)
282             goto Ldone;
283     }
284 Ldone:
285     p->check(TOKsemicolon);
286 
287     return s;
288 }
289 
290 /***********************************
291  * Parse and run semantic analysis on a GccAsmStatement.
292  * Params:
293  *      s  = gcc asm statement being parsed
294  *      sc = the scope where the asm statement is located
295  * Returns:
296  *      the completed gcc asm statement, or null if errors occurred
297  */
gccAsmSemantic(GccAsmStatement * s,Scope * sc)298 Statement *gccAsmSemantic(GccAsmStatement *s, Scope *sc)
299 {
300     //printf("GccAsmStatement::semantic()\n");
301     Parser p(sc->_module, (const utf8_t *)";", 1, false);
302 
303     // Make a safe copy of the token list before parsing.
304     Token *toklist = NULL;
305     Token **ptoklist = &toklist;
306 
307     for (Token *token = s->tokens; token; token = token->next)
308     {
309         *ptoklist = Token::alloc();
310         memcpy(*ptoklist, token, sizeof(Token));
311         ptoklist = &(*ptoklist)->next;
312         *ptoklist = NULL;
313     }
314     p.token = *toklist;
315     p.scanloc = s->loc;
316 
317     // Parse the gcc asm statement.
318     s = parseGccAsm(&p, s);
319     if (p.errors)
320         return NULL;
321     s->stc = sc->stc;
322 
323     // Fold the instruction template string.
324     s->insn = semantic(s->insn, sc);
325     s->insn = s->insn->ctfeInterpret();
326 
327     if (s->insn->op != TOKstring || ((StringExp *) s->insn)->sz != 1)
328         s->insn->error("asm instruction template must be a constant char string");
329 
330     if (s->labels && s->outputargs)
331         s->error("extended asm statements with labels cannot have output constraints");
332 
333     // Analyse all input and output operands.
334     if (s->args)
335     {
336         for (size_t i = 0; i < s->args->dim; i++)
337         {
338             Expression *e = (*s->args)[i];
339             e = semantic(e, sc);
340             // Check argument is a valid lvalue/rvalue.
341             if (i < s->outputargs)
342                 e = e->modifiableLvalue(sc, NULL);
343             else if (e->checkValue())
344                 e = new ErrorExp();
345             (*s->args)[i] = e;
346 
347             e = (*s->constraints)[i];
348             e = semantic(e, sc);
349             assert(e->op == TOKstring && ((StringExp *) e)->sz == 1);
350             (*s->constraints)[i] = e;
351         }
352     }
353 
354     // Analyse all clobbers.
355     if (s->clobbers)
356     {
357         for (size_t i = 0; i < s->clobbers->dim; i++)
358         {
359             Expression *e = (*s->clobbers)[i];
360             e = semantic(e, sc);
361             assert(e->op == TOKstring && ((StringExp *) e)->sz == 1);
362             (*s->clobbers)[i] = e;
363         }
364     }
365 
366     // Analyse all goto labels.
367     if (s->labels)
368     {
369         for (size_t i = 0; i < s->labels->dim; i++)
370         {
371             Identifier *ident = (*s->labels)[i];
372             GotoStatement *gs = new GotoStatement(s->loc, ident);
373             if (!s->gotos)
374                 s->gotos = new GotoStatements();
375             s->gotos->push(gs);
376             semantic(gs, sc);
377         }
378     }
379 
380     return s;
381 }
382