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