From 3c5cdad27858d366a6f5e25f19dcba127afeb983 Mon Sep 17 00:00:00 2001 From: Daniel Murphy Date: Sat, 11 Apr 2015 21:25:19 +1000 Subject: [PATCH] Add parser and some semantic support for gdc extended asm statements --- src/func.c | 1 + src/hdrgen.c | 58 ++++++++++ src/interpret.c | 24 ++++ src/magicport.json | 3 +- src/parse.c | 231 +++++++++++++++++++++++++++++++++++++ src/parse.h | 4 + src/statement.c | 96 +++++++++++++++ src/statement.h | 22 ++++ src/visitor.h | 2 + test/compilable/compile1.d | 12 ++ 10 files changed, 452 insertions(+), 1 deletion(-) diff --git a/src/func.c b/src/func.c index dc13c11f0358..263f3c940469 100644 --- a/src/func.c +++ b/src/func.c @@ -192,6 +192,7 @@ class StatementRewriteWalker : public Visitor visitStmt(s->statement); } void visit(AsmStatement *s) { } + void visit(ExtAsmStatement *s) { } void visit(ImportStatement *s) { } }; diff --git a/src/hdrgen.c b/src/hdrgen.c index ee78daea55bf..73ef334615ff 100644 --- a/src/hdrgen.c +++ b/src/hdrgen.c @@ -636,6 +636,64 @@ class PrettyPrintVisitor : public Visitor buf->writenl(); } + void visit(ExtAsmStatement *s) + { + buf->writestring("asm { "); + + if (s->insn) + buf->writestring(s->insn->toChars()); + + buf->writestring(" : "); + + if (s->args) + { + for (size_t i = 0; i < s->args->dim; i++) + { + Identifier *name = (*s->names)[i]; + Expression *constr = (*s->constraints)[i]; + Expression *arg = (*s->args)[i]; + + if (name) + { + buf->writestring("["); + buf->writestring(name->toChars()); + buf->writestring("] "); + } + + if (constr) + { + buf->writestring(constr->toChars()); + buf->writestring(" "); + } + + if (arg) + buf->writestring(arg->toChars()); + + if (i < s->outputargs - 1) + buf->writestring(", "); + else if (i == s->outputargs - 1) + buf->writestring(" : "); + else if (i < s->args->dim - 1) + buf->writestring(", "); + } + } + + if (s->clobbers) + { + buf->writestring(" : "); + for (size_t i = 0; i < s->clobbers->dim; i++) + { + Expression *clobber = (*s->clobbers)[i]; + buf->writestring(clobber->toChars()); + if (i < s->clobbers->dim - 1) + buf->writestring (", "); + } + } + + buf->writestring("; }"); + buf->writenl(); + } + void visit(ImportStatement *s) { for (size_t i = 0; i < s->imports->dim; i++) diff --git a/src/interpret.c b/src/interpret.c index 31f7d1ddbda5..7a817e510541 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -683,6 +683,14 @@ class CtfeCompiler : public Visitor // we can't compile asm statements } + void visit(ExtAsmStatement *s) + { + #if LOGCOMPILE + printf("%s ExtAsmStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // we can't compile ext asm statements + } + void ctfeCompile(Statement *s) { s->accept(this); @@ -1938,6 +1946,22 @@ class Interpreter : public Visitor result = CTFEExp::cantexp; } + void visit(ExtAsmStatement *s) + { + #if LOG + printf("%s ExtAsmStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } + + s->error("extended asm statements cannot be interpreted at compile time"); + result = CTFEExp::cantexp; + } + void visit(ImportStatement *s) { #if LOG diff --git a/src/magicport.json b/src/magicport.json index 8980ac3a7efc..e68051120a81 100644 --- a/src/magicport.json +++ b/src/magicport.json @@ -1857,7 +1857,8 @@ "struct LabelDsymbol", "struct AsmStatement", "struct CompoundAsmStatement", - "struct ImportStatement" + "struct ImportStatement", + "struct ExtAsmStatement" ] }, { diff --git a/src/parse.c b/src/parse.c index c306ca7b22a3..9fc3ae6d6944 100644 --- a/src/parse.c +++ b/src/parse.c @@ -5581,6 +5581,16 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc error("matching '}' expected, not end of file"); goto Lerror; + case TOKstring: + // If the first token is a string, parse as extended asm. + if (!toklist) + { + s = parseExtAsm(); + statements->push(s); + continue; + } + // ...else, drop through. + default: Ldefault: *ptoklist = Token::alloc(); @@ -5632,6 +5642,227 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc return s; } +/*********************************** + * Parse list of extended asm input or output operands. + * ExtAsmOperand: + * [Identifier] StringLiteral (Identifier), ... + */ +int Parser::parseExtAsmOperands(Expressions *args, Identifiers *names, Expressions *constraints) +{ + int numargs = 0; + + while (1) + { + Expression *arg = NULL; + Identifier *name = NULL; + Expression *constraint = NULL; + + switch (token.value) + { + case TOKsemicolon: + case TOKcolon: + case TOKeof: + return numargs; + + case TOKlbracket: + nextToken(); + if (token.value == TOKidentifier) + { + name = token.ident; + nextToken(); + } + else + { + error("expected identifier after '['"); + return numargs; + } + check(TOKrbracket); + // fall through + + default: + constraint = parsePrimaryExp(); + if (constraint->op != TOKstring) + { + error(constraint->loc, "expected constant string constraint for operand, not '%s'", constraint->toChars()); + goto Lerror; + } + arg = parseAssignExp(); + + args->push(arg); + names->push(name); + constraints->push(constraint); + numargs++; + + if (token.value == TOKcomma) + nextToken(); + break; + } + } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return numargs; +} + +/*********************************** + * Parse list of extended asm clobbers. + * ExtAsmClobbers: + * StringLiteral, ... + */ +Expressions *Parser::parseExtAsmClobbers() +{ + Expressions *clobbers = NULL; + + while (1) + { + Expression *clobber; + + switch (token.value) + { + case TOKsemicolon: + case TOKcolon: + case TOKeof: + return clobbers; + + case TOKstring: + clobber = parseAssignExp(); + if (!clobbers) + clobbers = new Expressions; + clobbers->push(clobber); + + if (token.value == TOKcomma) + nextToken(); + break; + + default: + error("expected constant string constraint for clobber name, not '%s'", token.toChars()); + goto Lerror; + } + } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return clobbers; +} + +/*********************************** + * Parse list of extended asm goto labels. + * ExtAsmGotoLabels: + * Identifier, ... + */ +Identifiers *Parser::parseExtAsmGotoLabels() +{ + Identifiers *labels = NULL; + + while (1) + { + switch (token.value) + { + case TOKsemicolon: + case TOKeof: + return labels; + + case TOKidentifier: + if (!labels) + labels = new Identifiers(); + labels->push(token.ident); + + if (nextToken() == TOKcomma) + nextToken(); + break; + + default: + error("expected identifier for goto label name, not '%s'", token.toChars()); + goto Lerror; + } + } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return labels; +} + +/*********************************** + * Parse an extended asm statement. + * ExtAsmStatement: + * asm { StringLiterals [ : InputOperands [ : OutputOperands [ : Clobbers [ : GotoLabels ] ] ] ] } + */ + +Statement *Parser::parseExtAsm() +{ + Expressions *args = NULL; + Identifiers *names = NULL; + Expressions *constraints = NULL; + int outputargs = 0; + Expressions *clobbers = NULL; + Identifiers *labels = NULL; + Loc loc = token.loc; + + Expression *insn = parseExpression(); + if (token.value == TOKsemicolon) + goto Ldone; + + for (int section = 0; section < 4; ++section) + { + check(TOKcolon); + + switch (section) + { + case 0: + if (!args) + { + args = new Expressions; + constraints = new Expressions; + names = new Identifiers; + } + outputargs = parseExtAsmOperands(args, names, constraints); + break; + + case 1: + parseExtAsmOperands(args, names, constraints); + break; + + case 2: + clobbers = parseExtAsmClobbers(); + break; + + case 3: + labels = parseExtAsmGotoLabels(); + break; + + default: + assert(0); + } + + switch (token.value) + { + case TOKsemicolon: + goto Ldone; + + case TOKeof: + error("matching '}' expected, not end of file"); + goto Ldone; + + default: + continue; + } + } + Ldone: + check(TOKsemicolon); + + return new ExtAsmStatement(loc, insn, args, names, + constraints, outputargs, clobbers, labels); +} + void Parser::check(TOK value) { check(token.loc, value); diff --git a/src/parse.h b/src/parse.h index 28e9452a6e46..6170b66f5637 100644 --- a/src/parse.h +++ b/src/parse.h @@ -126,6 +126,10 @@ class Parser : public Lexer void checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident); /** endPtr used for documented unittests */ Statement *parseStatement(int flags, const utf8_t** endPtr = NULL, Loc *pEndloc = NULL); + Statement *parseExtAsm(); + int parseExtAsmOperands(Expressions *args, Identifiers *names, Expressions *constraints); + Expressions *parseExtAsmClobbers(); + Identifiers *parseExtAsmGotoLabels(); Initializer *parseInitializer(); Expression *parseDefaultInitExp(); void check(Loc loc, TOK value); diff --git a/src/statement.c b/src/statement.c index a29b9e153077..5f1fb28d6fd5 100644 --- a/src/statement.c +++ b/src/statement.c @@ -193,6 +193,7 @@ bool Statement::comeFrom() void visit(DefaultStatement *s) { stop = true; } void visit(LabelStatement *s) { stop = true; } void visit(AsmStatement *s) { stop = true; } + void visit(ExtAsmStatement *s) { stop = true; } }; ComeFrom cf; @@ -5191,6 +5192,101 @@ Statement *AsmStatement::syntaxCopy() return new AsmStatement(loc, tokens); } +/************************ ExtStatement ***************************************/ + +ExtAsmStatement::ExtAsmStatement(Loc loc, Expression *insn, + Expressions *args, Identifiers *names, + Expressions *constraints, int outputargs, + Expressions *clobbers, Identifiers *labels) + : Statement(loc) +{ + this->insn = insn; + this->args = args; + this->names = names; + this->constraints = constraints; + this->outputargs = outputargs; + this->clobbers = clobbers; + this->labels = labels; + this->gotos = NULL; +} + +Statement *ExtAsmStatement::syntaxCopy() +{ + Expressions *c_args = Expression::arraySyntaxCopy(args); + Expressions *c_constraints = Expression::arraySyntaxCopy(constraints); + Expressions *c_clobbers = Expression::arraySyntaxCopy(clobbers); + + return new ExtAsmStatement(loc, insn->syntaxCopy(), c_args, names, + c_constraints, outputargs, c_clobbers, labels); +} + +Statement *ExtAsmStatement::semantic(Scope *sc) +{ + // Fold the instruction template string. + insn = insn->semantic(sc); + insn->optimize(WANTvalue); + + if (insn->op != TOKstring || ((StringExp *) insn)->sz != 1) + error("instruction template must be a constant char string"); + + // Analyse all input and output operands. + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + Expression *e = (*args)[i]; + e = e->semantic(sc); + if (i < outputargs) + e = e->modifiableLvalue(sc, NULL); + else + e = e->optimize(WANTvalue); + (*args)[i] = e; + + e = (*constraints)[i]; + e = e->semantic(sc); + e = e->optimize(WANTvalue); + if (e->op != TOKstring || ((StringExp *) e)->sz != 1) + error ("constraint must be a constant char string"); + (*constraints)[i] = e; + } + } + + // Fold all clobbers. + if (clobbers) + { + for (size_t i = 0; i < clobbers->dim; i++) + { + Expression *e = (*clobbers)[i]; + e = e->semantic(sc); + e = e->optimize(WANTvalue); + if (e->op != TOKstring || ((StringExp *) e)->sz != 1) + error("clobber specification must be a constant char string"); + (*clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (labels) + { + for (size_t i = 0; i < labels->dim; i++) + { + Identifier *ident = (*labels)[i]; + GotoStatement *s = new GotoStatement(loc, ident); + if (!gotos) + gotos = new GotoStatements(); + gotos->push(s); + s->semantic(sc); + } + } + +#ifdef IN_GCC + return this; +#else + error("Extended asm statements are not supported"); + return new ErrorStatement(); +#endif +} + /************************ CompoundAsmStatement ***************************************/ CompoundAsmStatement::CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc) diff --git a/src/statement.h b/src/statement.h index ce4a2f6d9424..705a15884592 100644 --- a/src/statement.h +++ b/src/statement.h @@ -770,4 +770,26 @@ class ImportStatement : public Statement void accept(Visitor *v) { v->visit(this); } }; +// Assembler instructions with D expression operands +class ExtAsmStatement : public Statement +{ +public: + Expression *insn; + Expressions *args; + Identifiers *names; + Expressions *constraints; // Array of StringExp's + int outputargs; + Expressions *clobbers; // Array of StringExp's + Identifiers *labels; + GotoStatements *gotos; + + ExtAsmStatement(Loc loc, Expression *insn, Expressions *args, + Identifiers *names, Expressions *constraints, + int outputargs, Expressions *clobbers, Identifiers *labels); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + + void accept(Visitor *v) { v->visit(this); } +}; + #endif /* DMD_STATEMENT_H */ diff --git a/src/visitor.h b/src/visitor.h index da315d10ada8..89de9cb5e141 100644 --- a/src/visitor.h +++ b/src/visitor.h @@ -54,6 +54,7 @@ class LabelStatement; class AsmStatement; class CompoundAsmStatement; class ImportStatement; +class ExtAsmStatement; class Type; class TypeError; @@ -341,6 +342,7 @@ class Visitor virtual void visit(AsmStatement *s) { visit((Statement *)s); } virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); } virtual void visit(ImportStatement *s) { visit((Statement *)s); } + virtual void visit(ExtAsmStatement *s) { visit((Statement *)s); } virtual void visit(Type *) { assert(0); } virtual void visit(TypeError *t) { visit((Type *)t); } diff --git a/test/compilable/compile1.d b/test/compilable/compile1.d index 250201a78a22..43eb01a1635b 100644 --- a/test/compilable/compile1.d +++ b/test/compilable/compile1.d @@ -670,6 +670,18 @@ import imports.a12506; private bool[9] r12506a = f12506!(i => true)(); // OK private immutable bool[9] r12506b = f12506!(i => true)(); // OK <- error +/***************************************************/ +// GDC extended asm statements should parse + +void testGDCasm() +{ + version(none) + { + asm { "notl %[iov]" : [iov] "=r" result : "0" v; } + asm { "nor %[oresult],%[iv],%[iv]" : [oresult] "=r" result : [iv] "r" v; } + } +} + /***************************************************/ // 12555