| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 2018-2019 by The D Language Foundation, All Rights Reserved |
| * written by Iain Buclaw |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/iasmgcc.c |
| */ |
| |
| /* Inline assembler for the GCC D compiler. |
| */ |
| |
| #include "scope.h" |
| #include "declaration.h" |
| #include "parse.h" |
| #include "statement.h" |
| |
| Expression *semantic(Expression *e, Scope *sc); |
| Statement *semantic(Statement *s, Scope *sc); |
| |
| /*********************************** |
| * Parse list of extended asm input or output operands. |
| * Grammar: |
| * | Operands: |
| * | SymbolicName(opt) StringLiteral AssignExpression |
| * | SymbolicName(opt) StringLiteral AssignExpression , Operands |
| * | |
| * | SymbolicName: |
| * | [ Identifier ] |
| * Params: |
| * p = parser state |
| * s = asm statement to parse |
| * Returns: |
| * number of operands added to the gcc asm statement |
| */ |
| static int parseExtAsmOperands(Parser *p, GccAsmStatement *s) |
| { |
| int numargs = 0; |
| |
| while (1) |
| { |
| Expression *arg = NULL; |
| Identifier *name = NULL; |
| Expression *constraint = NULL; |
| |
| switch (p->token.value) |
| { |
| case TOKsemicolon: |
| case TOKcolon: |
| case TOKeof: |
| return numargs; |
| |
| case TOKlbracket: |
| if (p->peekNext() == TOKidentifier) |
| { |
| p->nextToken(); |
| name = p->token.ident; |
| p->nextToken(); |
| } |
| else |
| { |
| p->error(s->loc, "expected identifier after `[`"); |
| goto Lerror; |
| } |
| p->check(TOKrbracket); |
| // fall through |
| |
| case TOKstring: |
| constraint = p->parsePrimaryExp(); |
| arg = p->parseAssignExp(); |
| |
| if (!s->args) |
| { |
| s->names = new Identifiers(); |
| s->constraints = new Expressions(); |
| s->args = new Expressions(); |
| } |
| s->names->push(name); |
| s->args->push(arg); |
| s->constraints->push(constraint); |
| numargs++; |
| |
| if (p->token.value == TOKcomma) |
| p->nextToken(); |
| break; |
| |
| default: |
| p->error("expected constant string constraint for operand, not `%s`", |
| p->token.toChars()); |
| goto Lerror; |
| } |
| } |
| Lerror: |
| while (p->token.value != TOKrcurly && |
| p->token.value != TOKsemicolon && |
| p->token.value != TOKeof) |
| p->nextToken(); |
| |
| return numargs; |
| } |
| |
| /*********************************** |
| * Parse list of extended asm clobbers. |
| * Grammar: |
| * | Clobbers: |
| * | StringLiteral |
| * | StringLiteral , Clobbers |
| * Params: |
| * p = parser state |
| * Returns: |
| * array of parsed clobber expressions |
| */ |
| static Expressions *parseExtAsmClobbers(Parser *p) |
| { |
| Expressions *clobbers = NULL; |
| |
| while (1) |
| { |
| Expression *clobber; |
| |
| switch (p->token.value) |
| { |
| case TOKsemicolon: |
| case TOKcolon: |
| case TOKeof: |
| return clobbers; |
| |
| case TOKstring: |
| clobber = p->parsePrimaryExp(); |
| if (!clobbers) |
| clobbers = new Expressions(); |
| clobbers->push(clobber); |
| |
| if (p->token.value == TOKcomma) |
| p->nextToken(); |
| break; |
| |
| default: |
| p->error("expected constant string constraint for clobber name, not `%s`", |
| p->token.toChars()); |
| goto Lerror; |
| } |
| } |
| Lerror: |
| while (p->token.value != TOKrcurly && |
| p->token.value != TOKsemicolon && |
| p->token.value != TOKeof) |
| p->nextToken(); |
| |
| return clobbers; |
| } |
| |
| /*********************************** |
| * Parse list of extended asm goto labels. |
| * Grammar: |
| * | GotoLabels: |
| * | Identifier |
| * | Identifier , GotoLabels |
| * Params: |
| * p = parser state |
| * Returns: |
| * array of parsed goto labels |
| */ |
| static Identifiers *parseExtAsmGotoLabels(Parser *p) |
| { |
| Identifiers *labels = NULL; |
| |
| while (1) |
| { |
| switch (p->token.value) |
| { |
| case TOKsemicolon: |
| case TOKeof: |
| return labels; |
| |
| case TOKidentifier: |
| if (!labels) |
| labels = new Identifiers(); |
| labels->push(p->token.ident); |
| |
| if (p->nextToken() == TOKcomma) |
| p->nextToken(); |
| break; |
| |
| default: |
| p->error("expected identifier for goto label name, not `%s`", |
| p->token.toChars()); |
| goto Lerror; |
| } |
| } |
| Lerror: |
| while (p->token.value != TOKrcurly && |
| p->token.value != TOKsemicolon && |
| p->token.value != TOKeof) |
| p->nextToken(); |
| |
| return labels; |
| } |
| |
| /*********************************** |
| * Parse a gcc asm statement. |
| * There are three forms of inline asm statements, basic, extended, and goto. |
| * Grammar: |
| * | AsmInstruction: |
| * | BasicAsmInstruction |
| * | ExtAsmInstruction |
| * | GotoAsmInstruction |
| * | |
| * | BasicAsmInstruction: |
| * | Expression |
| * | |
| * | ExtAsmInstruction: |
| * | Expression : Operands(opt) : Operands(opt) : Clobbers(opt) |
| * | |
| * | GotoAsmInstruction: |
| * | Expression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt) |
| * Params: |
| * p = parser state |
| * s = asm statement to parse |
| * Returns: |
| * the parsed gcc asm statement |
| */ |
| static GccAsmStatement *parseGccAsm(Parser *p, GccAsmStatement *s) |
| { |
| s->insn = p->parseExpression(); |
| if (p->token.value == TOKsemicolon || p->token.value == TOKeof) |
| goto Ldone; |
| |
| // No semicolon followed after instruction template, treat as extended asm. |
| for (int section = 0; section < 4; ++section) |
| { |
| p->check(TOKcolon); |
| |
| switch (section) |
| { |
| case 0: |
| s->outputargs = parseExtAsmOperands(p, s); |
| break; |
| |
| case 1: |
| parseExtAsmOperands(p, s); |
| break; |
| |
| case 2: |
| s->clobbers = parseExtAsmClobbers(p); |
| break; |
| |
| case 3: |
| s->labels = parseExtAsmGotoLabels(p); |
| break; |
| |
| default: |
| assert(0); |
| } |
| |
| if (p->token.value == TOKsemicolon || p->token.value == TOKeof) |
| goto Ldone; |
| } |
| Ldone: |
| p->check(TOKsemicolon); |
| |
| return s; |
| } |
| |
| /*********************************** |
| * Parse and run semantic analysis on a GccAsmStatement. |
| * Params: |
| * s = gcc asm statement being parsed |
| * sc = the scope where the asm statement is located |
| * Returns: |
| * the completed gcc asm statement, or null if errors occurred |
| */ |
| Statement *gccAsmSemantic(GccAsmStatement *s, Scope *sc) |
| { |
| //printf("GccAsmStatement::semantic()\n"); |
| Parser p(sc->_module, (const utf8_t *)";", 1, false); |
| |
| // Make a safe copy of the token list before parsing. |
| Token *toklist = NULL; |
| Token **ptoklist = &toklist; |
| |
| for (Token *token = s->tokens; token; token = token->next) |
| { |
| *ptoklist = Token::alloc(); |
| memcpy(*ptoklist, token, sizeof(Token)); |
| ptoklist = &(*ptoklist)->next; |
| *ptoklist = NULL; |
| } |
| p.token = *toklist; |
| p.scanloc = s->loc; |
| |
| // Parse the gcc asm statement. |
| s = parseGccAsm(&p, s); |
| if (p.errors) |
| return NULL; |
| s->stc = sc->stc; |
| |
| // Fold the instruction template string. |
| s->insn = semantic(s->insn, sc); |
| s->insn = s->insn->ctfeInterpret(); |
| |
| if (s->insn->op != TOKstring || ((StringExp *) s->insn)->sz != 1) |
| s->insn->error("asm instruction template must be a constant char string"); |
| |
| if (s->labels && s->outputargs) |
| s->error("extended asm statements with labels cannot have output constraints"); |
| |
| // Analyse all input and output operands. |
| if (s->args) |
| { |
| for (size_t i = 0; i < s->args->dim; i++) |
| { |
| Expression *e = (*s->args)[i]; |
| e = semantic(e, sc); |
| // Check argument is a valid lvalue/rvalue. |
| if (i < s->outputargs) |
| e = e->modifiableLvalue(sc, NULL); |
| else if (e->checkValue()) |
| e = new ErrorExp(); |
| (*s->args)[i] = e; |
| |
| e = (*s->constraints)[i]; |
| e = semantic(e, sc); |
| assert(e->op == TOKstring && ((StringExp *) e)->sz == 1); |
| (*s->constraints)[i] = e; |
| } |
| } |
| |
| // Analyse all clobbers. |
| if (s->clobbers) |
| { |
| for (size_t i = 0; i < s->clobbers->dim; i++) |
| { |
| Expression *e = (*s->clobbers)[i]; |
| e = semantic(e, sc); |
| assert(e->op == TOKstring && ((StringExp *) e)->sz == 1); |
| (*s->clobbers)[i] = e; |
| } |
| } |
| |
| // Analyse all goto labels. |
| if (s->labels) |
| { |
| for (size_t i = 0; i < s->labels->dim; i++) |
| { |
| Identifier *ident = (*s->labels)[i]; |
| GotoStatement *gs = new GotoStatement(s->loc, ident); |
| if (!s->gotos) |
| s->gotos = new GotoStatements(); |
| s->gotos->push(gs); |
| semantic(gs, sc); |
| } |
| } |
| |
| return s; |
| } |