From 50625d944102d66cd1372305ece28068a0573517 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sun, 5 Aug 2018 17:20:04 +0200 Subject: [PATCH 1/5] Move dmd/iasm.d to dmd/iasmdmd.d --- dub.sdl | 2 +- src/dmd/{iasm.d => iasmdmd.d} | 14 +++++--------- src/posix.mak | 2 +- src/win32.mak | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) rename src/dmd/{iasm.d => iasmdmd.d} (99%) diff --git a/dub.sdl b/dub.sdl index bb6179b79f2d..4a259ee71287 100644 --- a/dub.sdl +++ b/dub.sdl @@ -62,5 +62,5 @@ subPackage { versions "MARS" sourcePaths "src/dmd" excludedSourceFiles "src/dmd/backend/*" - excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasm,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" + excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasmdmd,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" } diff --git a/src/dmd/iasm.d b/src/dmd/iasmdmd.d similarity index 99% rename from src/dmd/iasm.d rename to src/dmd/iasmdmd.d index 39fd9efc4f7f..1e240423f135 100644 --- a/src/dmd/iasm.d +++ b/src/dmd/iasmdmd.d @@ -6,15 +6,15 @@ * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved * Authors: Mike Cote, John Micco and $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d, _iasm.d) - * Documentation: https://dlang.org/phobos/dmd_iasm.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasm.d + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmdmd.d, _iasmdmd.d) + * Documentation: https://dlang.org/phobos/dmd_iasmdmd.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmdmd.d */ -/* Inline assembler for the D programming language compiler +/* Inline assembler for the Digital Mars D compiler */ -module dmd.iasm; +module dmd.iasmdmd; import core.stdc.stdio; import core.stdc.stdarg; @@ -47,10 +47,6 @@ import dmd.backend.cdef; import dmd.backend.code; import dmd.backend.code_x86; import dmd.backend.global; -import dmd.backend.el; -import dmd.backend.type; -import dmd.backend.oper; -import dmd.backend.code; import dmd.backend.iasm; import dmd.backend.xmm; diff --git a/src/posix.mak b/src/posix.mak index 52b45ce1263e..d00ff6362f5a 100644 --- a/src/posix.mak +++ b/src/posix.mak @@ -323,7 +323,7 @@ GLUE_OBJS = G_GLUE_OBJS = $(addprefix $G/, $(GLUE_OBJS)) GLUE_SRCS=$(addsuffix .d, $(addprefix $D/,irstate toctype glue gluelayer todt tocsym toir dmsc \ - tocvdebug s2ir toobj e2ir eh iasm objc_glue)) + tocvdebug s2ir toobj e2ir eh iasmdmd objc_glue)) DMD_SRCS=$(FRONT_SRCS) $(GLUE_SRCS) $(BACK_HDRS) $(TK_HDRS) diff --git a/src/win32.mak b/src/win32.mak index 2260065e484e..8ee4c372489b 100644 --- a/src/win32.mak +++ b/src/win32.mak @@ -175,7 +175,7 @@ LEXER_ROOT=$(ROOT)/array.d $(ROOT)/ctfloat.d $(ROOT)/file.d $(ROOT)/filename.d \ PARSER_SRCS=$D/astbase.d $D/parsetimevisitor.d $D/parse.d $D/transitivevisitor.d $D/permissivevisitor.d $D/strictvisitor.d GLUE_SRCS=$D/irstate.d $D/toctype.d $D/glue.d $D/gluelayer.d $D/todt.d $D/tocsym.d $D/toir.d $D/dmsc.d \ - $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasm.d + $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasmdmd.d BACK_HDRS=$C/cc.d $C/cdef.d $C/cgcv.d $C/code.d $C/cv4.d $C/dt.d $C/el.d $C/global.d \ $C/obj.d $C/oper.d $C/outbuf.d $C/rtlsym.d $C/code_x86.d $C/iasm.d \ From b3c08210e1e4916695d3637fdb05693ee8f50c5e Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sun, 5 Aug 2018 18:46:58 +0200 Subject: [PATCH 2/5] Move main implementation of AsmStatement to InlineAsmStatement --- dub.sdl | 2 +- src/dmd/astbase.d | 15 ++++++++++- src/dmd/iasm.d | 52 ++++++++++++++++++++++++++++++++++++++ src/dmd/iasmdmd.d | 18 +++---------- src/dmd/parsetimevisitor.d | 3 +++ src/dmd/s2ir.d | 2 +- src/dmd/statement.d | 30 +++++++++++++++++++--- src/dmd/statement.h | 9 ++++++- src/dmd/strictvisitor.d | 1 + src/dmd/visitor.h | 4 +++ src/posix.mak | 2 +- src/win32.mak | 2 +- 12 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 src/dmd/iasm.d diff --git a/dub.sdl b/dub.sdl index 4a259ee71287..82794f2c7c2c 100644 --- a/dub.sdl +++ b/dub.sdl @@ -62,5 +62,5 @@ subPackage { versions "MARS" sourcePaths "src/dmd" excludedSourceFiles "src/dmd/backend/*" - excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasmdmd,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" + excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasm,iasmdmd,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" } diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index b695aed4c3b5..fd894a3fbdcb 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -2424,7 +2424,7 @@ struct ASTBase } } - extern (C++) final class AsmStatement : Statement + extern (C++) class AsmStatement : Statement { Token* tokens; @@ -2440,6 +2440,19 @@ struct ASTBase } } + extern (C++) final class InlineAsmStatement : AsmStatement + { + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc, tokens); + } + + override void accept(Visitor v) + { + v.visit(this); + } + } + extern (C++) class ExpStatement : Statement { Expression exp; diff --git a/src/dmd/iasm.d b/src/dmd/iasm.d new file mode 100644 index 000000000000..f1023d9b5729 --- /dev/null +++ b/src/dmd/iasm.d @@ -0,0 +1,52 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * Copyright (C) 2018 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d, _iasm.d) + * Documentation: https://dlang.org/phobos/dmd_iasm.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasm.d + */ + +/* Inline assembler for the D programming language compiler + */ + +module dmd.iasm; + +import dmd.dscope; +import dmd.func; +import dmd.statement; + +version (MARS) +{ + import dmd.iasmdmd; +} + +/************************ AsmStatement ***************************************/ + +extern(C++) Statement asmSemantic(AsmStatement s, Scope *sc) +{ + //printf("AsmStatement.semantic()\n"); + + FuncDeclaration fd = sc.parent.isFuncDeclaration(); + assert(fd); + + if (!s.tokens) + return null; + + // Assume assembler code takes care of setting the return value + sc.func.hasReturnExp |= 8; + + version (MARS) + { + auto ias = new InlineAsmStatement(s.loc, s.tokens); + return inlineAsmSemantic(ias, sc); + } + else + { + error("D inline assembler statements are not supported"); + return new ErrorStatement(); + } +} diff --git a/src/dmd/iasmdmd.d b/src/dmd/iasmdmd.d index 1e240423f135..efc6f90b9281 100644 --- a/src/dmd/iasmdmd.d +++ b/src/dmd/iasmdmd.d @@ -28,7 +28,6 @@ import dmd.dsymbol; import dmd.errors; import dmd.expression; import dmd.expressionsem; -import dmd.func; import dmd.globals; import dmd.id; import dmd.identifier; @@ -113,7 +112,7 @@ struct ASM_STATE LabelDsymbol psDollar; Dsymbol psLocalsize; bool bReturnax; - AsmStatement statement; + InlineAsmStatement statement; Scope* sc; Token* tok; TOK tokValue; @@ -4357,23 +4356,17 @@ extern (C++) public regm_t iasm_regs(block *bp) } -/************************ AsmStatement ***************************************/ +/************************ InlineAsmStatement *********************************/ -extern (C++) public Statement asmSemantic(AsmStatement s, Scope *sc) +public Statement inlineAsmSemantic(InlineAsmStatement s, Scope *sc) { - //printf("AsmStatement.semantic()\n"); + //printf("InlineAsmStatement.semantic()\n"); OP *o; OPND opnd1, opnd2, opnd3, opnd4; OPND* o1, o2, o3, o4; PTRNTAB ptb; int usNumops; - FuncDeclaration fd = sc.parent.isFuncDeclaration(); - - assert(fd); - - if (!s.tokens) - return null; asmstate.ucItype = 0; asmstate.bReturnax = false; @@ -4394,9 +4387,6 @@ version (none) // don't use bReturnax anymore, and will fail anyway if we use re asmstate.bReturnax = false; } - // Assume assembler code takes care of setting the return value - sc.func.hasReturnExp |= 8; - if (!asmstate.bInit) { asmstate.bInit = true; diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index 6b1d3673453b..1695aca3bb5b 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -126,6 +126,9 @@ public: void visit(AST.CompoundDeclarationStatement s) { visit(cast(AST.CompoundStatement)s); } void visit(AST.CompoundAsmStatement s) { visit(cast(AST.CompoundStatement)s); } + // AsmStatements + void visit(AST.InlineAsmStatement s) { visit(cast(AST.AsmStatement)s); } + //========================================================================================= // Types void visit(AST.TypeBasic t) { visit(cast(AST.Type)t); } diff --git a/src/dmd/s2ir.d b/src/dmd/s2ir.d index b173de162396..f7ecb6d21cd5 100644 --- a/src/dmd/s2ir.d +++ b/src/dmd/s2ir.d @@ -1512,7 +1512,7 @@ private extern (C++) class S2irVisitor : Visitor /**************************************** */ - override void visit(AsmStatement s) + override void visit(InlineAsmStatement s) // { .visit(irs, s); } { block *bpre; diff --git a/src/dmd/statement.d b/src/dmd/statement.d index 4dd5c2bb4cdd..7275d68af651 100644 --- a/src/dmd/statement.d +++ b/src/dmd/statement.d @@ -2379,9 +2379,32 @@ extern (C++) final class LabelDsymbol : Dsymbol /*********************************************************** */ -extern (C++) final class AsmStatement : Statement +extern (C++) class AsmStatement : Statement { Token* tokens; + + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc); + this.tokens = tokens; + } + + override Statement syntaxCopy() + { + return new AsmStatement(loc, tokens); + } + + override void accept(Visitor v) + { + v.visit(this); + } +} + +/*********************************************************** + * https://dlang.org/spec/iasm.html + */ +extern (C++) final class InlineAsmStatement : AsmStatement +{ code* asmcode; uint asmalign; // alignment of this statement uint regs; // mask of registers modified (must match regm_t in back end) @@ -2390,13 +2413,12 @@ extern (C++) final class AsmStatement : Statement extern (D) this(const ref Loc loc, Token* tokens) { - super(loc); - this.tokens = tokens; + super(loc, tokens); } override Statement syntaxCopy() { - return new AsmStatement(loc, tokens); + return new InlineAsmStatement(loc, tokens); } override void accept(Visitor v) diff --git a/src/dmd/statement.h b/src/dmd/statement.h index a01ce6c0f7f2..6a9fdcaf0d3f 100644 --- a/src/dmd/statement.h +++ b/src/dmd/statement.h @@ -677,6 +677,14 @@ class AsmStatement : public Statement { public: Token *tokens; + + Statement *syntaxCopy(); + void accept(Visitor *v) { v->visit(this); } +}; + +class InlineAsmStatement : public AsmStatement +{ +public: code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) @@ -684,7 +692,6 @@ class AsmStatement : public Statement bool naked; // true if function is to be naked Statement *syntaxCopy(); - void accept(Visitor *v) { v->visit(this); } }; diff --git a/src/dmd/strictvisitor.d b/src/dmd/strictvisitor.d index 442bd40a8416..7fd448fde179 100644 --- a/src/dmd/strictvisitor.d +++ b/src/dmd/strictvisitor.d @@ -100,6 +100,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.CompoundStatement) { assert(0); } override void visit(AST.CompoundDeclarationStatement) { assert(0); } override void visit(AST.CompoundAsmStatement) { assert(0); } + override void visit(AST.InlineAsmStatement) { assert(0); } override void visit(AST.Type) { assert(0); } override void visit(AST.TypeBasic) { assert(0); } override void visit(AST.TypeError) { assert(0); } diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index e9f755b15bba..bad84f3bf950 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -53,6 +53,7 @@ class DebugStatement; class GotoStatement; class LabelStatement; class AsmStatement; +class InlineAsmStatement; class CompoundAsmStatement; class ImportStatement; @@ -411,6 +412,9 @@ class ParseTimeVisitor virtual void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); } virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); } + // AsmStatements + virtual void visit(InlineAsmStatement *s) { visit((AsmStatement *)s); } + // Types virtual void visit(TypeBasic *t) { visit((Type *)t); } virtual void visit(TypeError *t) { visit((Type *)t); } diff --git a/src/posix.mak b/src/posix.mak index d00ff6362f5a..f857a2c16440 100644 --- a/src/posix.mak +++ b/src/posix.mak @@ -323,7 +323,7 @@ GLUE_OBJS = G_GLUE_OBJS = $(addprefix $G/, $(GLUE_OBJS)) GLUE_SRCS=$(addsuffix .d, $(addprefix $D/,irstate toctype glue gluelayer todt tocsym toir dmsc \ - tocvdebug s2ir toobj e2ir eh iasmdmd objc_glue)) + tocvdebug s2ir toobj e2ir eh iasm iasmdmd objc_glue)) DMD_SRCS=$(FRONT_SRCS) $(GLUE_SRCS) $(BACK_HDRS) $(TK_HDRS) diff --git a/src/win32.mak b/src/win32.mak index 8ee4c372489b..4f76633948f0 100644 --- a/src/win32.mak +++ b/src/win32.mak @@ -175,7 +175,7 @@ LEXER_ROOT=$(ROOT)/array.d $(ROOT)/ctfloat.d $(ROOT)/file.d $(ROOT)/filename.d \ PARSER_SRCS=$D/astbase.d $D/parsetimevisitor.d $D/parse.d $D/transitivevisitor.d $D/permissivevisitor.d $D/strictvisitor.d GLUE_SRCS=$D/irstate.d $D/toctype.d $D/glue.d $D/gluelayer.d $D/todt.d $D/tocsym.d $D/toir.d $D/dmsc.d \ - $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasmdmd.d + $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasm.d $D/iasmdmd.d BACK_HDRS=$C/cc.d $C/cdef.d $C/cgcv.d $C/code.d $C/cv4.d $C/dt.d $C/el.d $C/global.d \ $C/obj.d $C/oper.d $C/outbuf.d $C/rtlsym.d $C/code_x86.d $C/iasm.d \ From 89fdf188f10125053b40cdbbfe476a0ec1bb224a Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Mon, 6 Aug 2018 00:30:36 +0200 Subject: [PATCH 3/5] Propagate storage class in CompoundAsmStatement semantic --- src/dmd/statementsem.d | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dmd/statementsem.d b/src/dmd/statementsem.d index 89d864b73d09..b834bc387520 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -4028,6 +4028,9 @@ else override void visit(CompoundAsmStatement cas) { + // Apply postfix attributes of the asm block to each statement. + sc = sc.push(); + sc.stc |= cas.stc; foreach (ref s; *cas.statements) { s = s ? s.statementSemantic(sc) : null; @@ -4043,6 +4046,7 @@ else if (!(cas.stc & (STC.trusted | STC.safe)) && sc.func.setUnsafe()) cas.error("`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not"); + sc.pop(); result = cas; } From dad81c06bc6c8d6716cf6a17afb257f9056a905c Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Mon, 6 Aug 2018 00:30:52 +0200 Subject: [PATCH 4/5] Implement GccAsmStatement for gcc-style asm statements --- dub.sdl | 2 +- src/dmd/astbase.d | 13 ++ src/dmd/iasm.d | 9 + src/dmd/iasmgcc.d | 363 +++++++++++++++++++++++++++++++++++++ src/dmd/parsetimevisitor.d | 1 + src/dmd/statement.d | 32 ++++ src/dmd/statement.h | 18 ++ src/dmd/visitor.h | 2 + src/posix.mak | 2 +- src/win32.mak | 2 +- 10 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 src/dmd/iasmgcc.d diff --git a/dub.sdl b/dub.sdl index 82794f2c7c2c..834407d203f1 100644 --- a/dub.sdl +++ b/dub.sdl @@ -62,5 +62,5 @@ subPackage { versions "MARS" sourcePaths "src/dmd" excludedSourceFiles "src/dmd/backend/*" - excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasm,iasmdmd,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" + excludedSourceFiles "src/dmd/{e2ir,eh,glue,iasm,iasmdmd,iasmgcc,objc_glue,s2ir,tocsym,toctype,toobj,todt,toir}.d" } diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index fd894a3fbdcb..b95927895447 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -2453,6 +2453,19 @@ struct ASTBase } } + extern (C++) final class GccAsmStatement : AsmStatement + { + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc, tokens); + } + + override void accept(Visitor v) + { + v.visit(this); + } + } + extern (C++) class ExpStatement : Statement { Expression exp; diff --git a/src/dmd/iasm.d b/src/dmd/iasm.d index f1023d9b5729..8dd0a13f9d9b 100644 --- a/src/dmd/iasm.d +++ b/src/dmd/iasm.d @@ -23,6 +23,10 @@ version (MARS) { import dmd.iasmdmd; } +else version (IN_GCC) +{ + import dmd.iasmgcc; +} /************************ AsmStatement ***************************************/ @@ -44,6 +48,11 @@ extern(C++) Statement asmSemantic(AsmStatement s, Scope *sc) auto ias = new InlineAsmStatement(s.loc, s.tokens); return inlineAsmSemantic(ias, sc); } + else version (IN_GCC) + { + auto eas = new GccAsmStatement(s.loc, s.tokens); + return gccAsmSemantic(eas, sc); + } else { error("D inline assembler statements are not supported"); diff --git a/src/dmd/iasmgcc.d b/src/dmd/iasmgcc.d new file mode 100644 index 000000000000..976c9e430ce8 --- /dev/null +++ b/src/dmd/iasmgcc.d @@ -0,0 +1,363 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * Copyright (C) 2018 by The D Language Foundation, All Rights Reserved + * Authors: Iain Buclaw + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d) + * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d + */ + +/* Inline assembler for the GCC D compiler. + */ + +module dmd.iasmgcc; + +import core.stdc.string; + +import dmd.arraytypes; +import dmd.astcodegen; +import dmd.dscope; +import dmd.expression; +import dmd.expressionsem; +import dmd.identifier; +import dmd.globals; +import dmd.parse; +import dmd.tokens; +import dmd.statement; +import dmd.statementsem; + +private: + +/*********************************** + * 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 + */ +int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s) +{ + int numargs = 0; + + while (1) + { + Expression arg; + Identifier name; + Expression constraint; + + switch (p.token.value) + { + case TOK.semicolon: + case TOK.colon: + case TOK.endOfFile: + return numargs; + + case TOK.leftBracket: + if (p.peekNext() == TOK.identifier) + { + p.nextToken(); + name = p.token.ident; + p.nextToken(); + } + else + { + p.error(s.loc, "expected identifier after `[`"); + goto Lerror; + } + p.check(TOK.rightBracket); + goto case; + + case TOK.string_: + 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 == TOK.comma) + p.nextToken(); + break; + + default: + p.error("expected constant string constraint for operand, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + 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 + */ +Expressions *parseExtAsmClobbers(Parser)(Parser p) +{ + Expressions *clobbers; + + while (1) + { + Expression clobber; + + switch (p.token.value) + { + case TOK.semicolon: + case TOK.colon: + case TOK.endOfFile: + return clobbers; + + case TOK.string_: + clobber = p.parsePrimaryExp(); + if (!clobbers) + clobbers = new Expressions(); + clobbers.push(clobber); + + if (p.token.value == TOK.comma) + 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 != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + 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 + */ +Identifiers *parseExtAsmGotoLabels(Parser)(Parser p) +{ + Identifiers *labels; + + while (1) + { + switch (p.token.value) + { + case TOK.semicolon: + case TOK.endOfFile: + return labels; + + case TOK.identifier: + if (!labels) + labels = new Identifiers(); + labels.push(p.token.ident); + + if (p.nextToken() == TOK.comma) + p.nextToken(); + break; + + default: + p.error("expected identifier for goto label name, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + 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 + */ +GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s) +{ + s.insn = p.parseExpression(); + if (p.token.value == TOK.semicolon) + goto Ldone; + + // No semicolon followed after instruction template, treat as extended asm. + foreach (section; 0 .. 4) + { + p.check(TOK.colon); + + final switch (section) + { + case 0: + s.outputargs = p.parseExtAsmOperands(s); + break; + + case 1: + p.parseExtAsmOperands(s); + break; + + case 2: + s.clobbers = p.parseExtAsmClobbers(); + break; + + case 3: + s.labels = p.parseExtAsmGotoLabels(); + break; + } + + if (p.token.value == TOK.semicolon) + goto Ldone; + } +Ldone: + p.check(TOK.semicolon); + + 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 + */ +public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) +{ + //printf("GccAsmStatement.semantic()\n"); + scope p = new Parser!ASTCodegen(sc._module, ";", 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, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + } + p.token = *toklist; + + // Parse the gcc asm statement. + s = p.parseGccAsm(s); + if (p.errors) + return null; + s.stc = sc.stc; + + // Fold the instruction template string. + if (s.insn.op == TOK.mixin_) + s.insn = (cast(CompileExp)s.insn).e1; + s.insn = semanticString(sc, s.insn, "asm instruction template"); + + 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) + { + foreach (i; 0 .. s.args.dim) + { + 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 == TOK.string_ && (cast(StringExp) e).sz == 1); + (*s.constraints)[i] = e; + } + } + + // Analyse all clobbers. + if (s.clobbers) + { + foreach (i; 0 .. s.clobbers.dim) + { + Expression e = (*s.clobbers)[i]; + e = e.expressionSemantic(sc); + assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1); + (*s.clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (s.labels) + { + foreach (i; 0 .. s.labels.dim) + { + 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); + } + } + + return s; +} diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index 1695aca3bb5b..22741a6f7ac9 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -128,6 +128,7 @@ public: // AsmStatements void visit(AST.InlineAsmStatement s) { visit(cast(AST.AsmStatement)s); } + void visit(AST.GccAsmStatement s) { visit(cast(AST.AsmStatement)s); } //========================================================================================= // Types diff --git a/src/dmd/statement.d b/src/dmd/statement.d index 7275d68af651..af4322093b92 100644 --- a/src/dmd/statement.d +++ b/src/dmd/statement.d @@ -2427,6 +2427,38 @@ extern (C++) final class InlineAsmStatement : AsmStatement } } +/*********************************************************** + * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * Assembler instructions with D expression operands. + */ +extern (C++) final class GccAsmStatement : AsmStatement +{ + StorageClass stc; // attributes of the asm {} block + Expression insn; // string expression that is the template for assembler code + Expressions* args; // input and output operands of the statement + uint outputargs; // of the operands in 'args', the number of output operands + Identifiers* names; // list of symbolic names for the operands + Expressions* constraints; // list of string constants specifying constraints on operands + Expressions* clobbers; // list of string constants specifying clobbers and scratch registers + Identifiers* labels; // list of goto labels + GotoStatements* gotos; // of the goto labels, the equivalent statements they represent + + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc, tokens); + } + + override Statement syntaxCopy() + { + return new GccAsmStatement(loc, tokens); + } + + 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 6a9fdcaf0d3f..70a0986a1d84 100644 --- a/src/dmd/statement.h +++ b/src/dmd/statement.h @@ -695,6 +695,24 @@ class InlineAsmStatement : public AsmStatement void accept(Visitor *v) { v->visit(this); } }; +// A GCC asm statement - assembler instructions with D expression operands +class GccAsmStatement : public AsmStatement +{ +public: + StorageClass stc; // attributes of the asm {} block + Expression *insn; // string expression that is the template for assembler code + Expressions *args; // input and output operands of the statement + unsigned outputargs; // of the operands in 'args', the number of output operands + Identifiers *names; // list of symbolic names for the operands + Expressions *constraints; // list of string constants specifying constraints on operands + Expressions *clobbers; // list of string constants specifying clobbers and scratch registers + Identifiers *labels; // list of goto labels + GotoStatements *gotos; // of the goto labels, the equivalent statements they represent + + Statement *syntaxCopy(); + void accept(Visitor *v) { v->visit(this); } +}; + // a complete asm {} block class CompoundAsmStatement : public CompoundStatement { diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index bad84f3bf950..3818a1af79dd 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -54,6 +54,7 @@ class GotoStatement; class LabelStatement; class AsmStatement; class InlineAsmStatement; +class GccAsmStatement; class CompoundAsmStatement; class ImportStatement; @@ -414,6 +415,7 @@ class ParseTimeVisitor // AsmStatements virtual void visit(InlineAsmStatement *s) { visit((AsmStatement *)s); } + virtual void visit(GccAsmStatement *s) { visit((AsmStatement *)s); } // Types virtual void visit(TypeBasic *t) { visit((Type *)t); } diff --git a/src/posix.mak b/src/posix.mak index f857a2c16440..9e4fce0d0c58 100644 --- a/src/posix.mak +++ b/src/posix.mak @@ -323,7 +323,7 @@ GLUE_OBJS = G_GLUE_OBJS = $(addprefix $G/, $(GLUE_OBJS)) GLUE_SRCS=$(addsuffix .d, $(addprefix $D/,irstate toctype glue gluelayer todt tocsym toir dmsc \ - tocvdebug s2ir toobj e2ir eh iasm iasmdmd objc_glue)) + tocvdebug s2ir toobj e2ir eh iasm iasmdmd iasmgcc objc_glue)) DMD_SRCS=$(FRONT_SRCS) $(GLUE_SRCS) $(BACK_HDRS) $(TK_HDRS) diff --git a/src/win32.mak b/src/win32.mak index 4f76633948f0..7f948e56e431 100644 --- a/src/win32.mak +++ b/src/win32.mak @@ -175,7 +175,7 @@ LEXER_ROOT=$(ROOT)/array.d $(ROOT)/ctfloat.d $(ROOT)/file.d $(ROOT)/filename.d \ PARSER_SRCS=$D/astbase.d $D/parsetimevisitor.d $D/parse.d $D/transitivevisitor.d $D/permissivevisitor.d $D/strictvisitor.d GLUE_SRCS=$D/irstate.d $D/toctype.d $D/glue.d $D/gluelayer.d $D/todt.d $D/tocsym.d $D/toir.d $D/dmsc.d \ - $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasm.d $D/iasmdmd.d + $D/tocvdebug.d $D/s2ir.d $D/toobj.d $D/e2ir.d $D/objc_glue.d $D/eh.d $D/iasm.d $D/iasmdmd.d $D/iasmgcc.d BACK_HDRS=$C/cc.d $C/cdef.d $C/cgcv.d $C/code.d $C/cv4.d $C/dt.d $C/el.d $C/global.d \ $C/obj.d $C/oper.d $C/outbuf.d $C/rtlsym.d $C/code_x86.d $C/iasm.d \ From 79a81ae3280af9eb9356f3a01675c6dae8587b64 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Tue, 14 Aug 2018 09:44:04 +0200 Subject: [PATCH 5/5] Add unittests for parseGccAsm entrypoint --- src/dmd/iasmgcc.d | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/dmd/iasmgcc.d b/src/dmd/iasmgcc.d index 976c9e430ce8..a8902759ac94 100644 --- a/src/dmd/iasmgcc.d +++ b/src/dmd/iasmgcc.d @@ -361,3 +361,89 @@ public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) return s; } + +unittest +{ + uint errors = global.startGagging(); + + // Immitates asmSemantic if version = IN_GCC. + static int semanticAsm(Token* tokens) + { + scope gas = new GccAsmStatement(Loc.initial, tokens); + scope p = new Parser!ASTCodegen(null, ";", false); + p.token = *tokens; + p.parseGccAsm(gas); + return p.errors; + } + + // Immitates parseStatement for asm statements. + static void parseAsm(string input, bool expectError) + { + // Generate tokens from input test. + scope p = new Parser!ASTCodegen(null, input, false); + p.nextToken(); + + Token* toklist = null; + Token** ptoklist = &toklist; + p.check(TOK.asm_); + p.check(TOK.leftCurly); + while (1) + { + if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile) + break; + *ptoklist = Token.alloc(); + memcpy(*ptoklist, &p.token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + p.nextToken(); + } + p.check(TOK.rightCurly); + + auto res = semanticAsm(toklist); + assert(res == 0 || expectError); + } + + /// Assembly Tests, all should pass. + /// Note: Frontend is not initialized, use only strings and identifiers. + immutable string[] passAsmTests = [ + // Basic asm statement + q{ asm { "nop"; + } }, + + // Extended asm statement + q{ asm { "cpuid" + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) + : "a" input; + } }, + + // Assembly with symbolic names + q{ asm { "bts %[base], %[offset]" + : [base] "+rm" *ptr, + : [offset] "Ir" bitnum; + } }, + + // Assembly with clobbers + q{ asm { "cpuid" + : "=a" a + : "a" input + : "ebx", "ecx", "edx"; + } }, + + // Goto asm statement + q{ asm { "jmp %l0" + : + : + : + : Ljmplabel; + } }, + + // Any CTFE-able string allowed as instruction template. + q{ asm { generateAsm() + } }, + ]; + + foreach (test; passAsmTests) + parseAsm(test, false); + + global.endGagging(errors); +}