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