From 1528dc35400282f23bb85839936d1beb01e5d302 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sun, 31 Dec 2017 02:26:50 +0100 Subject: [PATCH] Add parser and some semantic support for gdc extended asm statements --- src/dmd/dinterpret.d | 31 +++++ src/dmd/hdrgen.d | 61 ++++++++++ src/dmd/parse.d | 236 +++++++++++++++++++++++++++++++++++++ src/dmd/parsetimevisitor.d | 1 + src/dmd/statement.d | 54 +++++++++ src/dmd/statement.h | 23 ++++ src/dmd/statementsem.d | 65 ++++++++++ src/dmd/visitor.h | 6 + 8 files changed, 477 insertions(+) diff --git a/src/dmd/dinterpret.d b/src/dmd/dinterpret.d index 41eaeb6921b7..13f67e575541 100644 --- a/src/dmd/dinterpret.d +++ b/src/dmd/dinterpret.d @@ -599,6 +599,18 @@ public: // we can't compile asm statements } + version (IN_GCC) + { + override void visit(ExtAsmStatement s) + { + debug (LOGCOMPILE) + { + printf("%s ExtAsmStatement::ctfeCompile\n", s.loc.toChars()); + } + // we can't compile extended asm statements + } + } + void ctfeCompile(Statement s) { s.accept(this); @@ -1931,6 +1943,25 @@ public: result = CTFEExp.cantexp; } + version (IN_GCC) + { + override void visit(ExtAsmStatement s) + { + debug (LOG) + { + printf("%s ExtAsmStatement::interpret()\n", s.loc.toChars()); + } + 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; + } + } + override void visit(ImportStatement s) { debug (LOG) diff --git a/src/dmd/hdrgen.d b/src/dmd/hdrgen.d index b90b4436391f..cec27cc79b46 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -685,6 +685,67 @@ public: buf.writenl(); } + version (IN_GCC) + { + override void visit(ExtAsmStatement s) + { + buf.writestring ("gcc 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(); + } + } + override void visit(ImportStatement s) { foreach (imp; *s.imports) diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 1b40bedaef0c..771de4de8d02 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -5989,6 +5989,20 @@ final class Parser(AST) : Lexer error("matching `}` expected, not end of file"); goto Lerror; + static if (IN_GCC) + { + case TOKlparen: + case TOKstring: + // If the first token is a string or '(', parse as extended asm. + if (!toklist) + { + s = parseExtAsm(stc); + statements.push(s); + continue; + } + goto default; + } + default: *ptoklist = Token.alloc(); memcpy(*ptoklist, &token, Token.sizeof); @@ -6034,6 +6048,228 @@ final class Parser(AST) : Lexer return s; } + static if (IN_GCC) + { + /*********************************** + * Parse list of extended asm input or output operands. + * ExtAsmOperand: + * [Identifier] StringLiteral (Identifier), ... + */ + int parseExtAsmOperands(AST.Expressions* args, AST.Identifiers* names, AST.Expressions* constraints) + { + int numargs = 0; + + while (1) + { + AST.Expression arg; + Identifier name; + AST.Expression constraint; + + 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); + goto default; // 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, ... + */ + AST.Expressions *parseExtAsmClobbers() + { + AST.Expressions *clobbers; + + while (1) + { + AST.Expression clobber; + + switch (token.value) + { + case TOKsemicolon: + case TOKcolon: + case TOKeof: + return clobbers; + + case TOKstring: + clobber = parseAssignExp(); + if (!clobbers) + clobbers = new AST.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, ... + */ + AST.Identifiers *parseExtAsmGotoLabels() + { + AST.Identifiers *labels; + + while (1) + { + switch (token.value) + { + case TOKsemicolon: + case TOKeof: + return labels; + + case TOKidentifier: + if (!labels) + labels = new AST.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 ] ] ] ] } + */ + + AST.Statement parseExtAsm(StorageClass stc) + { + AST.Expressions *args; + AST.Identifiers *names; + AST.Expressions *constraints; + int outputargs = 0; + AST.Expressions *clobbers; + AST.Identifiers *labels; + Loc loc = token.loc; + + AST.Expression insn = parseExpression(); + if (token.value == TOKsemicolon) + goto Ldone; + + for (int section = 0; section < 4; ++section) + { + check(TOKcolon); + + final switch (section) + { + case 0: + if (!args) + { + args = new AST.Expressions(); + constraints = new AST.Expressions(); + names = new AST.Identifiers(); + } + outputargs = parseExtAsmOperands(args, names, constraints); + break; + + case 1: + parseExtAsmOperands(args, names, constraints); + break; + + case 2: + clobbers = parseExtAsmClobbers(); + break; + + case 3: + labels = parseExtAsmGotoLabels(); + break; + } + + 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 AST.ExtAsmStatement(loc, stc, insn, args, names, + constraints, outputargs, clobbers, labels); + } + } + /***************************************** * Parse initializer for variable declaration. */ diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index 12397e9ef89c..a15189d092c4 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -116,6 +116,7 @@ public: void visit(AST.TryFinallyStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ThrowStatement s) { visit(cast(AST.Statement)s); } void visit(AST.AsmStatement s) { visit(cast(AST.Statement)s); } + version (IN_GCC) void visit(AST.ExtAsmStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ExpStatement s) { visit(cast(AST.Statement)s); } void visit(AST.CompoundStatement s) { visit(cast(AST.Statement)s); } diff --git a/src/dmd/statement.d b/src/dmd/statement.d index 010850d16021..08ce8e24ca89 100644 --- a/src/dmd/statement.d +++ b/src/dmd/statement.d @@ -224,6 +224,14 @@ extern (C++) abstract class Statement : RootObject { stop = true; } + + version (IN_GCC) + { + override void visit(ExtAsmStatement s) + { + stop = true; + } + } } scope ComeFrom cf = new ComeFrom(); @@ -2404,6 +2412,52 @@ extern (C++) final class AsmStatement : Statement } } +/*********************************************************** + */ +version (IN_GCC) +{ + extern (C++) final class ExtAsmStatement : Statement + { + StorageClass stc; + Expression insn; + Expressions *args; + Identifiers *names; + Expressions *constraints; // Array of StringExp's + uint outputargs; + Expressions *clobbers; // Array of StringExp's + Identifiers *labels; + GotoStatements *gotos; + + extern (D) this(Loc loc, StorageClass stc, Expression insn, + Expressions *args, Identifiers *names, + Expressions *constraints, int outputargs, + Expressions *clobbers, Identifiers *labels) + { + super(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; + } + + override Statement syntaxCopy() + { + return new ExtAsmStatement(loc, stc, insn.syntaxCopy(), Expression.arraySyntaxCopy(args), names, + Expression.arraySyntaxCopy(constraints), outputargs, + Expression.arraySyntaxCopy(clobbers), labels); + } + + override void accept(Visitor v) + { + v.visit(this); + } + } +} + /*********************************************************** * a complete asm {} block */ diff --git a/src/dmd/statement.h b/src/dmd/statement.h index 2196696fceda..0bae009a76c7 100644 --- a/src/dmd/statement.h +++ b/src/dmd/statement.h @@ -687,6 +687,29 @@ class AsmStatement : public Statement void accept(Visitor *v) { v->visit(this); } }; +#ifdef IN_GCC + +// Assembler instructions with D expression operands +class ExtAsmStatement : public Statement +{ +public: + StorageClass stc; + Expression *insn; + Expressions *args; + Identifiers *names; + Expressions *constraints; // Array of StringExp's + unsigned outputargs; + Expressions *clobbers; // Array of StringExp's + Identifiers *labels; + GotoStatements *gotos; + + Statement *syntaxCopy(); + + void accept(Visitor *v) { v->visit(this); } +}; + +#endif + // a complete asm {} block class CompoundAsmStatement : public CompoundStatement { diff --git a/src/dmd/statementsem.d b/src/dmd/statementsem.d index ac0f3783c3d9..9cd50864e975 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -3924,6 +3924,71 @@ else result = asmSemantic(s, sc); } + version (IN_GCC) + { + override void visit(ExtAsmStatement s) + { + // Fold the instruction template string. + s.insn = s.insn.expressionSemantic(sc); + s.insn.ctfeInterpret(); + + if (s.insn.op != TOKstring || (cast(StringExp) s.insn).sz != 1) + s.insn.error("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 = e.expressionSemantic(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 = e.expressionSemantic(sc); + assert(e.op == TOKstring && (cast(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 = e.expressionSemantic(sc); + assert(e.op == TOKstring && (cast(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); + gs.statementSemantic(sc); + } + } + + result = s; + } + } + override void visit(CompoundAsmStatement cas) { foreach (ref s; *cas.statements) diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index 69b0e9cad6e5..8459f3af398a 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -54,6 +54,9 @@ class DebugStatement; class GotoStatement; class LabelStatement; class AsmStatement; +#ifdef IN_GCC +class ExtAsmStatement; +#endif class CompoundAsmStatement; class ImportStatement; @@ -404,6 +407,9 @@ class Visitor virtual void visit(TryFinallyStatement *s) { visit((Statement *)s); } virtual void visit(ThrowStatement *s) { visit((Statement *)s); } virtual void visit(AsmStatement *s) { visit((Statement *)s); } +#ifdef IN_GCC + virtual void visit(ExtAsmStatement *s) { visit((Statement *)s); } +#endif virtual void visit(ExpStatement *s) { visit((Statement *)s); } virtual void visit(CompoundStatement *s) { visit((Statement *)s); }