In git head, a new feature "asm statement with attributes" is introduced.
https://github.com/D-Programming-Language/dmd/pull/4033
It's a straight feature that an asm statement should be handled as throwable, unsafe, and impure by default, and
if you want to assume it as nothrow safe pure, asm statement should have additonal attibutes.
asm {nop;} // this statement is throwable, unsafe, impure
asm nothrow @safe pure {nop;} // this statement is nothrow, safe, and pure
But, when you want to implementing a function by using inline assembler, and you want to make the function nothrow, you need to repeat the nothrow attribute two or more.
auto foo(int a) nothrow // 1st
{
asm nothrow // 2nd
{/* operations */}
int b = a * 2; // if you mix regular D code
asm nothrow // 3nd
{/* operations */}
}
The more you use high level code (==D code), the more redundancy it requires.
But, we already *know* that an asm statement is definitely throwable, unsafe, and impure. It's obvious so compiler will never understand the semantics of inline assembler code. And asm statements explicitly appear in code, so the following semantic would be much better in practice.
1. By default, asm statement is handled as throwable, unsafe, and impure. Therefore attribute inference
will make the enclosing function as such.
void foo()() { asm{nop}; }
static assert(is(typeof(&foo!()) == void function()));
// typeof(foo!()) neither nothrow, @safe, nor pure
2. if enclosing function is explicitly marked with attributes,
D compiler can handle that the all asm statements in function body are assumed as such.
void foo()() nothrow pure @trusted { asm{nop;} }
static assert(is(typeof(&foo!()) == void function() nothrow pure @safe));
void bar() nothrow pure @trusted { asm{nop;} } // no error
Of course, the attribute violation out of the asm statement should be checked by compiler.
void bar() nothrow {
asm{nop;} // assmued to nothrow
throw new Exception(""); // error, nothrow violation
}
Comment #1 by k.hara.pg — 2014-10-08T15:09:09Z
For the memory safety, asm statement with @trusted attribute is still useful.
void foo(int* p) @safe {
asm @trusted {/*...*/} // this is assumed as safe.
(*p + 1) = 10; // pointer arithmetic is disallowed in safe code
}
And more, I'd disallow asm @safe {/*...*/} , because we already have the best attribute @trusted to assume the code as such.
Comment #2 by code — 2014-10-08T15:29:13Z
(In reply to Kenji Hara from comment #1)
> And more, I'd disallow asm @safe {/*...*/} , because we already have the
> best attribute @trusted to assume the code as such.
Disallowing @safe makes sense.
I don't agree with reusing the containing function attributes though because it breaks the let me try if I can make this function pure/nothrow/@safe pattern.
When I add an attribute to a function with an asm statement it would silently compile even though noone checked the attributes. Think of a github pull request diff where you see added attributes on a function but not the asm statement withing the body.
Comment #3 by yebblies — 2014-10-08T15:47:27Z
(In reply to Kenji Hara from comment #1)
>
> And more, I'd disallow asm @safe {/*...*/} , because we already have the
> best attribute @trusted to assume the code as such.
Absolutely.
I don't agree with the rest of the proposal. Asm statements are fairly infrequent and are rather dangerous, so I don't think the overhead of having to explicitly attribute them is too much. They're usually nothrow, but purity and safety are not such a given.
And templated functions often have attributes inferred, and then you'd have to mark the asm blocks by hand. This will be inconsistent.
Grepping phobos shows ~ less than 100 asm statements, mostly in math and bigint functions. I don't think use of asm statements is prolific enough to trade explicitly guaranteeing attributes hold for less redundancy.
Comment #4 by k.hara.pg — 2014-10-08T15:54:18Z
(In reply to Martin Nowak from comment #2)
> (In reply to Kenji Hara from comment #1)
> > And more, I'd disallow asm @safe {/*...*/} , because we already have the
> > best attribute @trusted to assume the code as such.
>
> Disallowing @safe makes sense.
>
> I don't agree with reusing the containing function attributes though because
> it breaks the let me try if I can make this function pure/nothrow/@safe
> pattern.
I don't argue the current feature `asm attributes {...}`. It is still useful with attribute inference.
void foo(T)(int value) {
asm nothrow @trusted pure {...} // the baseline of attributes
T t = value; // foo's attribute will depends on the T's constructor
}
> When I add an attribute to a function with an asm statement it would
> silently compile even though noone checked the attributes.
It's not silent, because the code will explicitly display the trusting of attribute.
1. The function attributes are explicit if it is specified.
2. asm statement is always explicit, different from attribute violations in normal D code.
> Think of a github
> pull request diff where you see added attributes on a function but not the
> asm statement withing the body.
For example, we can just mark the casImpl function in core.druntime as pure nothrow @nogc instead of marking the all asm statements in its body.
https://github.com/D-Programming-Language/druntime/pull/975/files
The many diff is not theoretical issue. But practically problematic.
Comment #5 by k.hara.pg — 2014-10-08T16:16:13Z
(In reply to yebblies from comment #3)
> I don't agree with the rest of the proposal. Asm statements are fairly
> infrequent and are rather dangerous, so I don't think the overhead of having
> to explicitly attribute them is too much. They're usually nothrow, but
> purity and safety are not such a given.
It could be more serious. If someone want to write a kernel code with D, mixing inline assembler and regular D code may be painful. It would be a serious flaw against the goal "D is a system language".
> And templated functions often have attributes inferred, and then you'd have
> to mark the asm blocks by hand. This will be inconsistent.
Yes, in template functions, asm with attributes is still useful.
> Grepping phobos shows ~ less than 100 asm statements, mostly in math and
> bigint functions. I don't think use of asm statements is prolific enough to
> trade explicitly guaranteeing attributes hold for less redundancy.
It is case by case. *In phobos*, it is less than 100 asm, but we would already have large code base we are unknown. I believe D will be a widespread language, so we should imagine the quite big use cases even they are not visible.
Comment #6 by k.hara.pg — 2014-10-08T16:17:12Z
OK, I found a better explanation.
If an asm statement does not have attributes, its attribute will be inferred from the *explicitly specified* attributes on enclosing function.
void f1() {
asm {...}
// f1 has no explicit attributes, so asm is inferred as system, impure, throwable
}
void f2()() {
asm {...}
// f2 has no explicit attributes, so asm is inferred as system, impure, throwable
}
void f3() @safe pure {
asm {...}
// f3 has explicit @safe attribute, so asm is inferred as @trusted, pure, but throwable
}
void f4()() @truested nothrow {
asm {...}
// f4 has explicit @safe attribute, so asm is inferred as @trusted, nothrow , but impure
}
Comment #7 by yebblies — 2014-10-08T16:31:01Z
(In reply to Kenji Hara from comment #6)
>
> void f3() @safe pure {
> asm {...}
> // f3 has explicit @safe attribute, so asm is inferred as @trusted, pure,
> but throwable
> }
>
This is a problem. f3 is _not_ @safe, it is @trusted. But unlike other violations, you won't find it by grepping for @trusted.
> It could be more serious. If someone want to write a kernel code with D,
> mixing inline assembler and regular D code may be painful. It would be a
> serious flaw against the goal "D is a system language".
I don't think it is a serious flaw against that goal. Everything is still possible, and not very difficult either. I would much rather have the attributes be opt-in than opt-out at the statement level, where the compiler can't decide automatically.
>
> It is case by case. *In phobos*, it is less than 100 asm, but we would
> already have large code base we are unknown. I believe D will be a
> widespread language, so we should imagine the quite big use cases even they
> are not visible.
This isn't an argument that these uses exist and are widespread, and therefore worth catering too. The proposed change is also backwards-compatible.
Comment #8 by k.hara.pg — 2014-10-08T16:38:36Z
(In reply to yebblies from comment #7)
> (In reply to Kenji Hara from comment #6)
> >
> > void f3() @safe pure {
> > asm {...}
> > // f3 has explicit @safe attribute, so asm is inferred as @trusted, pure,
> > but throwable
> > }
> >
>
> This is a problem. f3 is _not_ @safe, it is @trusted. But unlike other
> violations, you won't find it by grepping for @trusted.
OK, I change the behavior in the case.
void fsafe() @safe pure {
asm {...} // f3 has @safe attribute, so this asm will report an error
asm @trusted {...} // OK
}
void ftrusted() @trusted {
asm {...}
// f3 has explicit @trusted attribute, so asm is inferred as @trusted
}
Comment #9 by yebblies — 2014-10-08T17:01:18Z
(In reply to Kenji Hara from comment #8)
>
> OK, I change the behavior in the case.
>
> void fsafe() @safe pure {
> asm {...} // f3 has @safe attribute, so this asm will report an error
> asm @trusted {...} // OK
> }
>
> void ftrusted() @trusted {
> asm {...}
> // f3 has explicit @trusted attribute, so asm is inferred as @trusted
> }
That's certainly better, but overall I still don't think this is worthwhile.
Comment #10 by k.hara.pg — 2014-10-08T17:27:37Z
(In reply to yebblies from comment #9)
> That's certainly better, but overall I still don't think this is worthwhile.
In this case we can reduce amount of attributes. Isn't it a benefit?
Moreover, issue 12979 had accepted following code.
void foo() nothrow { asm{...}; }
It was accidental behavior, but this enhancement will accept it properly and will reduce code breaking with the next release.
Comment #11 by yebblies — 2014-10-08T17:44:17Z
(In reply to Kenji Hara from comment #10)
> (In reply to yebblies from comment #9)
> > That's certainly better, but overall I still don't think this is worthwhile.
>
> In this case we can reduce amount of attributes. Isn't it a benefit?
Yes, but...
> Moreover, issue 12979 had accepted following code.
>
> void foo() nothrow { asm{...}; }
>
> It was accidental behavior, but this enhancement will accept it properly and
> will reduce code breaking with the next release.
I don't think that code should be valid. nothrow on a function signature should not be enough to opt-out of nothrow checking for the asm. This is more important than reducing the number of attributes.
Comment #12 by code — 2014-10-08T18:48:40Z
The basic problem with this is using inference on something that cannot be inferred.
void seeminglyPure() {
// ...
// many lines of code
// ...
asm {
// impure instruction
}
}
Now when someone adds pure to seeminglyPure it would silently break.
Comment #13 by ibuclaw — 2014-10-12T18:05:29Z
(In reply to Martin Nowak from comment #12)
> The basic problem with this is using inference on something that cannot be
> inferred.
>
> void seeminglyPure() {
> // ...
> // many lines of code
> // ...
> asm {
> // impure instruction
> }
> }
>
> Now when someone adds pure to seeminglyPure it would silently break.
You could perhaps get away with inference with naked functions, as they shouldn't have any high-level code inside.
Comment #14 by robert.schadek — 2024-12-13T18:30:43Z