view gcc/d/dmd/iasmgcc.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
line wrap: on
line source


/* 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;
}