【问题标题】:D - friendlier asm syntactic sugar for GDC (Dlang)D - GDC (Dlang) 的更友好的 asm 语法糖
【发布时间】:2016-08-19 02:24:13
【问题描述】:

我有一个想法来简化使用 GDC 的扩展 asm 语法创建 D plus asm 代码的过程。我想摆脱在整个地方插入 \n\t 标记的需要,例如,使用单独的字符串并让 D 编译器将它们连接起来。但我愿意接受其他建议。我的尝试失败了,因为不幸的是连接 D 字符串在 GDC 中不起作用在编译时,我需要 CTFE。正如您所料,这块糖的成本为零。

我怀疑我需要用 mixin 做点什么。有关去哪里以及如何留在 CTFE 内的任何提示?

【问题讨论】:

  • 有几种字符串样式,如q{...},保留插入的换行符的反引号。此外,我建议避免使用汇编块,它们在编译时不起作用,而且它们也难以阅读和维护。您首先要解决的问题是什么?
  • 我一直在为新的超轻量级包装器编写一些编译器内在函数,这些包装器围绕单个 x86-64 指令,用于针对 GDC 用户的库中。这些函数允许在应用程序代码中以零开销插入(例如 haswell)指令,并且如果新指令可用的寻址模式,甚至可以使用全范围与调用代码无缝匹配。
  • 感谢您的提示,我忘记了替代文字字符串样式。我是一位经验丰富的专业 asm 和 C 程序员,但对 D 来说是新手。我还想参数化使用模板,以允许以更友好的形式选择 AT&T 语法或 Intel 语法作为模板的参数,我没有这样做在原帖中没有提到。我认为这意味着更多的串联。 (或混合?)
  • 基本上,我的问题归结为两部分? 1. 可以在编译时拼接字符串吗?并且:2. 如果相关,这里如何利用 mixin?
  • 1) 确定,D 让它变得如此简单以至于你看不到它:enum myString = "a" ~"b";

标签: d inline-assembly gdc


【解决方案1】:

GDC 存在缺陷,因为扩展内联 ASM 中的 AssemblerTemplate 假定 是编译时生成字符串,但实际上并非如此。你可以做的是生成字符串,把所有的 ASM 东西放在它周围,然后把它混进去。我一直在使用类似的东西来实现自定义系统调用(仍然是内联的)。

module test;

template joinASMStatements(T...) {
    static if (T.length == 0) enum joinASMStatements = "";
    else static if (T.length == 1) enum joinASMStatements = T[0];
    else enum joinASMStatements = joinASMStatements!(T[0..$/2]) ~ "\\n\\t" ~  joinASMStatements!(T[$/2..$]);
}

void main() {
    ulong dst, src = 20;
    enum statements = joinASMStatements!("mov %[dst], %[src]");
    mixin("asm {" ~ statements ~ " : [dst] \"rm,r\" dst : [src] \"ri,rmi\" src }");
}

但坦率地说,这看起来很可怕。创建一个模板来为您处理所有这些会更容易,更美观,它需要一个字符串数组。您可以在模板中实现额外的东西来处理某些操作码并根据它们自动添加约束。如果您愿意,这将使代码也可以在 DMD 和 LDC 上运行。你可以使用一点编译时魔法来解决这一切。 (编辑)这确实有效。

module test2;

import std.traits: AliasSeq;

// Input operand type
struct IOp(string _name) {
    string constraints; // A set of constraints. This is the whole thing.
    string asmName; // A label to be given to the operand (the "[<name>]" thing)
    enum name = _name; // Inner usage, to ease accessing `_name`.
}

// Output operand type
struct OOp(string _name) {
    // For variable details, see IOp comments.
    string constraints;
    string asmName;
    enum name = _name;
}

// type for register (and "cc" and "memory") clobbers
struct Clobber(string _reg) {enum reg = _reg;}

// type for goto labels
struct Goto(string _goto) {enum name = _goto;} // note that `goto` is a D keyword.

// filters out types as S!(string blah)
template filterOp(alias S, T...) {
    static if (T.length == 0) alias filterOp = AliasSeq!();
    else static if (T.length == 1) {
        static if (is(typeof(T[0]) : S!(N), string N))
            alias filterOp = AliasSeq!(T[0]);
        else
            alias filterOp = AliasSeq!();
    } else
        alias filterOp = AliasSeq!(filterOp!(S, T[0..$/2]), filterOp!(S, T[$/2..$]));
}

// joiner function for input and output operands.
template joinOps(T...) {
    static if (T.length == 0) enum joinOps = "";
    else static if (T.length == 1) enum joinOps = ((T[0].asmName != "")?"[" ~ T[0].asmName ~ "] ":"") ~ "\"" ~ T[0].constraints ~ "\" " ~ T[0].name; // The .name unescapes the name
    else enum joinOps = joinOps!(T[0..$/2]) ~ ", " ~ joinOps!(T[$/2..$]);
}

// joiner function for clobbers
template joinClobbers(T...) {
    static if (T.length == 0) enum joinClobbers = "";
    else static if (T.length == 1) enum joinClobbers = "\"" ~ T[0].reg ~ "\"";
    else enum joinClobbers = joinClobbers!(T[0..$/2]) ~ ", " ~ joinClobbers!(T[$/2..$]);
}

// joiner function for goto labels
template joinGotos(T...) {
    static if (T.length == 0) enum joinGotos = "";
    else static if (T.length == 1) enum joinGotos = T[0].name; // Here the label is unescaped
    else enum joinGotos = joinGotos!(T[0..$/2]) ~ ", " ~ joinGotos!(T[$/2..$]); // Recursively executes itself on halves of the input. Eventually, the halves become lengths of `1` or `0`, and they are actually evaluated.
}

// joiner function for instructions.
template joinInstrs(string[] instrs) {
    static if (instrs.length == 0) enum joinInstrs = "";
    else static if (instrs.length == 1) enum joinInstrs = instrs[0];
    else enum joinInstrs = joinInstrs!(instrs[0..$/2]) ~ "\\n\\t" ~ joinInstrs!(instrs[$/2..$]);
}

// complete assembly generator function. Output is to be mixed in.
template ASM(string[] ops, T...) {
    enum iops = joinOps!(filterOp!(IOp, T));
    enum oops = joinOps!(filterOp!(OOp, T));
    enum clobbers = joinClobbers!(filterOp!(Clobber, T));
    enum gotos = joinGotos!(filterOp!(Goto, T));
    enum instrs = "\"" ~ joinInstrs!(ops) ~ "\"";
    enum ASM = "asm { " ~ instrs ~ " : " ~ oops ~ " : " ~ iops ~ " : " ~ clobbers ~ " : " ~ gotos ~ "; }";
}

void main() {
    ulong src = 24, dst;
    mixin(ASM!(["mov %[dst], %[src]"], IOp!"src"("ri,rmi", "src"), OOp!"dst"("=rm,r", "dst")));
}

注意事项:

  • 您可能希望组合IOpOOp 并根据约束区分输入和输出,而不是将= 添加到输出约束(有关有效的输出约束,请参见GCC 文档)。其他所有内容都将构成共享 struct Op 或类似名称下的输入操作数。
    • 这是我的第一次尝试,更好的方法可以做到这一点并简化我忽略的代码。

顺便说一句,谢谢你让我想到这个!我现在需要为我自己的东西实现它。

【讨论】:

    猜你喜欢
    • 2017-07-21
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多