diff mbox

Make basic asm implicitly clobber memory

Message ID AM4PR07MB157101A1194D2A45C841E506E47C0@AM4PR07MB1571.eurprd07.prod.outlook.com
State New
Headers show

Commit Message

Bernd Edlinger May 5, 2016, 5:29 p.m. UTC
Hi!

this patch is inspired by recent discussion about basic asm:

Currently a basic asm is an instruction scheduling barrier,
but not a memory barrier, and most surprising, basic asm
does _not_ implicitly clobber CC on targets where
extended asm always implicitly clobbers CC, even if
nothing is in the clobber section.

This patch makes basic asm implicitly clobber CC on certain
targets, and makes the basic asm implicitly clobber memory,
but no general registers, which is what could be expected.

This is however only done for basic asm with non-empty
assembler string, which is in sync with David's proposed
basic asm warnings patch.

Due to the change in the tree representation, where
ASM_INPUT can now be the first element of a
PARALLEL block with the implicit clobber elements,
there are some changes necessary.

Most of the changes in the middle end, were necessary
because extract_asm_operands can not be used to find out
if a PARALLEL block is an asm statement, but in most cases
asm_noperands can be used instead.

There are also changes necessary in two targets: pa, and ia64.
I have successfully built cross-compilers for these targets.

Boot-strapped and reg-tested on x86_64-pc-linux-gnu
OK for trunk?


Thanks
Bernd.
gcc/
2016-05-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	PR c/24414
	* cfgexpand.c (expand_asm_loc): Remove handling for ADDR_EXPR.
	Implicitly clobber memory for basic asm with non-empty assembler
	string.  Use targetm.md_asm_adjust also here.
	* compare-emim.c (arithmetic_flags_clobber_p): Use asm_noperands here.
	* final.c (final_scan_insn): Handle basic asm in PARALLEL block.
	* gimple.c (gimple_asm_clobbers_memory_p): Handle basic asm with
	non-empty assembler string.
	* ira.c (compute_regs_asm_clobbered): Use asm_noperands here.
	* recog.c (asm_noperands): Handle basic asm in PARALLEL block.
	(decode_asm_operands): Handle basic asm in PARALLEL block.
	(extract_insn): Handle basic asm in PARALLEL block.
	* doc/extend.texi: Mention new behavior of basic asm.
	* config/ia64/ia64 (rtx_needs_barrier): Handle ASM_INPUT here.
	* config/pa/pa.c (branch_to_delay_slot_p, branch_needs_nop_p,
	branch_needs_nop_p): Use asm_noperands.

gcc/testsuite/
2016-05-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	PR c/24414
	* gcc.target/i386/pr24414.c: New test.

Comments

David Wohlferd May 6, 2016, 6:35 a.m. UTC | #1
On 5/5/2016 10:29 AM, Bernd Edlinger wrote:
> Hi!
>
> this patch is inspired by recent discussion about basic asm:
>
> Currently a basic asm is an instruction scheduling barrier,
> but not a memory barrier, and most surprising, basic asm
> does _not_ implicitly clobber CC on targets where
> extended asm always implicitly clobbers CC, even if
> nothing is in the clobber section.
>
> This patch makes basic asm implicitly clobber CC on certain
> targets, and makes the basic asm implicitly clobber memory,
> but no general registers, which is what could be expected.
>
> This is however only done for basic asm with non-empty
> assembler string, which is in sync with David's proposed
> basic asm warnings patch.
>
> Due to the change in the tree representation, where
> ASM_INPUT can now be the first element of a
> PARALLEL block with the implicit clobber elements,
> there are some changes necessary.
>
> Most of the changes in the middle end, were necessary
> because extract_asm_operands can not be used to find out
> if a PARALLEL block is an asm statement, but in most cases
> asm_noperands can be used instead.
>
> There are also changes necessary in two targets: pa, and ia64.
> I have successfully built cross-compilers for these targets.
>
> Boot-strapped and reg-tested on x86_64-pc-linux-gnu
> OK for trunk?

A few questions:

1) I'm not clear precisely what problem this patch fixes.  It's true 
that some people have incorrectly assumed that basic asm clobbers memory 
and this change would fix their code.  But some people also incorrectly 
assume it clobbers registers.  I assume that's why Jeff Law proposed 
making basic asm "an opaque blob that read/write/clobber any register or 
memory location."  Do we have enough problem reports from users to know 
which is the real solution here?

2) The -Wbasic-asm warning patch wasn't approved for v6.  If we are 
going to change this behavior now, is it time?

3) I assume there are good reasons why extended asm can't be used at top 
level.  Will adding these clobbers cause those problems in basic asm too?

4) There are more basic asm docs that need to change: "It also does not 
know about side effects of the assembler code, such as modifications to 
memory or registers. Unlike some compilers, GCC assumes that no changes 
to either memory or registers occur. This assumption may change in a 
future release."

dw
Bernd Edlinger May 6, 2016, 1:07 p.m. UTC | #2
On 05/06/16 08:35, David Wohlferd wrote:
> On 5/5/2016 10:29 AM, Bernd Edlinger wrote:
>> Hi!
>>
>> this patch is inspired by recent discussion about basic asm:
>>
>> Currently a basic asm is an instruction scheduling barrier,
>> but not a memory barrier, and most surprising, basic asm
>> does _not_ implicitly clobber CC on targets where
>> extended asm always implicitly clobbers CC, even if
>> nothing is in the clobber section.
>>
>> This patch makes basic asm implicitly clobber CC on certain
>> targets, and makes the basic asm implicitly clobber memory,
>> but no general registers, which is what could be expected.
>>
>> This is however only done for basic asm with non-empty
>> assembler string, which is in sync with David's proposed
>> basic asm warnings patch.
>>
>> Due to the change in the tree representation, where
>> ASM_INPUT can now be the first element of a
>> PARALLEL block with the implicit clobber elements,
>> there are some changes necessary.
>>
>> Most of the changes in the middle end, were necessary
>> because extract_asm_operands can not be used to find out
>> if a PARALLEL block is an asm statement, but in most cases
>> asm_noperands can be used instead.
>>
>> There are also changes necessary in two targets: pa, and ia64.
>> I have successfully built cross-compilers for these targets.
>>
>> Boot-strapped and reg-tested on x86_64-pc-linux-gnu
>> OK for trunk?
>
> A few questions:
>
> 1) I'm not clear precisely what problem this patch fixes.  It's true
> that some people have incorrectly assumed that basic asm clobbers memory
> and this change would fix their code.  But some people also incorrectly
> assume it clobbers registers.  I assume that's why Jeff Law proposed
> making basic asm "an opaque blob that read/write/clobber any register or
> memory location."  Do we have enough problem reports from users to know
> which is the real solution here?
>

Whenever I do something for gcc I do it actually for myself, in my own
best interest.  And this is no exception.

The way I see it, is this: in simple cases a basic asm behaves as if
it would clobber memory, because of the way Jeff implemented the
asm handling in sched-deps.c some 20 years ago.

Look for ASM_INPUT where we have this comment:
"Traditional and volatile asm instructions must be considered to use
  and clobber all hard registers, all pseudo-registers and all of
  memory."

The assumption that it is OK to clobber memory in a basic asm will only
break if the asm statement is inlined in a loop, and that may happen
unexpectedly, when gcc rolls out new optimizations.
That's why I consider this to be security relevant.

But OTOH you see immediately that all general registers are in use
by gcc, unless you declare a variable like
register int eax __asm__("rax");
then it is perfectly OK to use rax in a basic asm of course.

And if we want to have implicitly clobbered registers, like the
diab compiler handles the basic asm, then this patch will
make it possible to add a target hook that clobbers additional
registers for basic asm.


> 2) The -Wbasic-asm warning patch wasn't approved for v6.  If we are
> going to change this behavior now, is it time?
>

Yes. We have stage1 for gcc-7 development, I can't think of a better
time for it.
I would even say, the -Wbasic-asm warning patch makes more sense now,
because we could warn, that the basich asm clobbers memory, which it
did not previously.

> 3) I assume there are good reasons why extended asm can't be used at top
> level.  Will adding these clobbers cause those problems in basic asm too?
>

No, these don't come along here, and nothing should change for them.

> 4) There are more basic asm docs that need to change: "It also does not
> know about side effects of the assembler code, such as modifications to
> memory or registers. Unlike some compilers, GCC assumes that no changes
> to either memory or registers occur. This assumption may change in a
> future release."
>

Yes, I should change that sentence too.

Maybe this way:

"Unlike some compilers, GCC assumes that no changes to registers
occur.  This assumption may change in a future release."



Thanks
Bernd.

> dw
David Wohlferd May 6, 2016, 11:33 p.m. UTC | #3
>> A few questions:
>>
>> 1) I'm not clear precisely what problem this patch fixes.  It's true
>> that some people have incorrectly assumed that basic asm clobbers memory
>> and this change would fix their code.  But some people also incorrectly
>> assume it clobbers registers.  I assume that's why Jeff Law proposed
>> making basic asm "an opaque blob that read/write/clobber any register or
>> memory location."  Do we have enough problem reports from users to know
>> which is the real solution here?
>>
> Whenever I do something for gcc I do it actually for myself, in my own
> best interest.  And this is no exception.

Seems fair.  You are the one putting the time in to change it.

But do you have actual code that is affected by this?  You can't really 
be planning to wait until v7 is released to have your projects work 
correctly?

> The way I see it, is this: in simple cases a basic asm behaves as if
> it would clobber memory, because of the way Jeff implemented the
> asm handling in sched-deps.c some 20 years ago.
>
> Look for ASM_INPUT where we have this comment:
> "Traditional and volatile asm instructions must be considered to use
>    and clobber all hard registers, all pseudo-registers and all of
>    memory."
>
> The assumption that it is OK to clobber memory in a basic asm will only
> break if the asm statement is inlined in a loop, and that may happen
> unexpectedly, when gcc rolls out new optimizations.
> That's why I consider this to be security relevant.

I'm not sure I follow.  Do you fear that gcc could mistakenly move the 
asm into a nearby loop during optimization (resulting in who-knows-what 
results)?  Or is there some way that any basic asm in a loop could have 
some kind of a problem?

> But OTOH you see immediately that all general registers are in use
> by gcc, unless you declare a variable like
> register int eax __asm__("rax");
> then it is perfectly OK to use rax in a basic asm of course.

According to the docs, that is only supported for global registers. The 
docs for local register variables explicitly say that it can't be used 
as input/outputs for basic asm.

> And if we want to have implicitly clobbered registers, like the
> diab compiler handles the basic asm, then this patch will
> make it possible to add a target hook that clobbers additional
> registers for basic asm.

I think we should try to avoid changing the semantics in v7 for memory 
and then changing them again in v8 for registers.

IOW, if I see some basic asm in a project (or on stack overflow/blog as 
a code fragment), should I assume it was intended for v6 semantics? v7? 
v8?  People often copy this stuff without understanding what it does.  
The more often the semantics change, the harder it is to use correctly 
and maintain.

>> 2) The -Wbasic-asm warning patch wasn't approved for v6.  If we are
>> going to change this behavior now, is it time?
>>
> Yes. We have stage1 for gcc-7 development, I can't think of a better
> time for it.
> I would even say, the -Wbasic-asm warning patch makes more sense now,
> because we could warn, that the basich asm clobbers memory, which it
> did not previously.

After your patch has been checked in, I'll re-submit this.

>> 4) There are more basic asm docs that need to change: "It also does not
>> know about side effects of the assembler code, such as modifications to
>> memory or registers. Unlike some compilers, GCC assumes that no changes
>> to either memory or registers occur. This assumption may change in a
>> future release."
>>
> Yes, I should change that sentence too.
>
> Maybe this way:
>
> "Unlike some compilers, GCC assumes that no changes to registers
> occur.  This assumption may change in a future release."

Is it worth describing the fact that the semantics have changed here?  
Something like "Before v7, gcc assumed no changes were made to memory."  
I guess users can "figure it out" by reading the v6 docs and comparing 
it to v7.  But if the semantic change has introduced a problem that 
someone is trying to debug, this could help them track it down.

Also, I'm kind of hoping that v7 is the 'future release.'  If we are 
going to change the clobbers, I'd hope that we'd do it all at one time, 
rather than spreading it out across several releases.

If no one is prepared to step up and implement (or at least defend) the 
idea of clobbering registers, I'd like to see the "This assumption may 
change in a future release" part removed.  Since (theoretically) 
anything can change at any time, the only reason this text made sense 
was because a change was imminent.  If that's no longer true, it's time 
for it to go.

dw
Bernd Edlinger May 7, 2016, 9:08 a.m. UTC | #4
On 05/07/16 01:33, David Wohlferd wrote:
>
>>> A few questions:
>>>
>>> 1) I'm not clear precisely what problem this patch fixes.  It's true
>>> that some people have incorrectly assumed that basic asm clobbers memory
>>> and this change would fix their code.  But some people also incorrectly
>>> assume it clobbers registers.  I assume that's why Jeff Law proposed
>>> making basic asm "an opaque blob that read/write/clobber any register or
>>> memory location."  Do we have enough problem reports from users to know
>>> which is the real solution here?
>>>
>> Whenever I do something for gcc I do it actually for myself, in my own
>> best interest.  And this is no exception.
>
> Seems fair.  You are the one putting the time in to change it.
>
> But do you have actual code that is affected by this?  You can't really
> be planning to wait until v7 is released to have your projects work
> correctly?
>

No, I can wait.
This is a long term investment in overall software reliability.

>> The way I see it, is this: in simple cases a basic asm behaves as if
>> it would clobber memory, because of the way Jeff implemented the
>> asm handling in sched-deps.c some 20 years ago.
>>
>> Look for ASM_INPUT where we have this comment:
>> "Traditional and volatile asm instructions must be considered to use
>>    and clobber all hard registers, all pseudo-registers and all of
>>    memory."
>>
>> The assumption that it is OK to clobber memory in a basic asm will only
>> break if the asm statement is inlined in a loop, and that may happen
>> unexpectedly, when gcc rolls out new optimizations.
>> That's why I consider this to be security relevant.
>
> I'm not sure I follow.  Do you fear that gcc could mistakenly move the
> asm into a nearby loop during optimization (resulting in who-knows-what
> results)?  Or is there some way that any basic asm in a loop could have
> some kind of a problem?
>

It is a risk, that who-knows-what will happen, unexpectedly.

Bernd.

>> But OTOH you see immediately that all general registers are in use
>> by gcc, unless you declare a variable like
>> register int eax __asm__("rax");
>> then it is perfectly OK to use rax in a basic asm of course.
>
> According to the docs, that is only supported for global registers. The
> docs for local register variables explicitly say that it can't be used
> as input/outputs for basic asm.
>
>> And if we want to have implicitly clobbered registers, like the
>> diab compiler handles the basic asm, then this patch will
>> make it possible to add a target hook that clobbers additional
>> registers for basic asm.
>
> I think we should try to avoid changing the semantics in v7 for memory
> and then changing them again in v8 for registers.
>
> IOW, if I see some basic asm in a project (or on stack overflow/blog as
> a code fragment), should I assume it was intended for v6 semantics? v7?
> v8?  People often copy this stuff without understanding what it does.
> The more often the semantics change, the harder it is to use correctly
> and maintain.
>
>>> 2) The -Wbasic-asm warning patch wasn't approved for v6.  If we are
>>> going to change this behavior now, is it time?
>>>
>> Yes. We have stage1 for gcc-7 development, I can't think of a better
>> time for it.
>> I would even say, the -Wbasic-asm warning patch makes more sense now,
>> because we could warn, that the basich asm clobbers memory, which it
>> did not previously.
>
> After your patch has been checked in, I'll re-submit this.
>
>>> 4) There are more basic asm docs that need to change: "It also does not
>>> know about side effects of the assembler code, such as modifications to
>>> memory or registers. Unlike some compilers, GCC assumes that no changes
>>> to either memory or registers occur. This assumption may change in a
>>> future release."
>>>
>> Yes, I should change that sentence too.
>>
>> Maybe this way:
>>
>> "Unlike some compilers, GCC assumes that no changes to registers
>> occur.  This assumption may change in a future release."
>
> Is it worth describing the fact that the semantics have changed here?
> Something like "Before v7, gcc assumed no changes were made to memory."
> I guess users can "figure it out" by reading the v6 docs and comparing
> it to v7.  But if the semantic change has introduced a problem that
> someone is trying to debug, this could help them track it down.
>
> Also, I'm kind of hoping that v7 is the 'future release.'  If we are
> going to change the clobbers, I'd hope that we'd do it all at one time,
> rather than spreading it out across several releases.
>
> If no one is prepared to step up and implement (or at least defend) the
> idea of clobbering registers, I'd like to see the "This assumption may
> change in a future release" part removed.  Since (theoretically)
> anything can change at any time, the only reason this text made sense
> was because a change was imminent.  If that's no longer true, it's time
> for it to go.
>
> dw
Andrew Haley May 7, 2016, 5:38 p.m. UTC | #5
On 06/05/16 07:35, David Wohlferd wrote:

> 1) I'm not clear precisely what problem this patch fixes.  It's true
> that some people have incorrectly assumed that basic asm clobbers
> memory and this change would fix their code.  But some people also
> incorrectly assume it clobbers registers.  I assume that's why Jeff
> Law proposed making basic asm "an opaque blob that
> read/write/clobber any register or memory location."

A few more things:

Jeff Law did propose this, but it's impossible to do because it
inevitably causes reload failures.

My argument in support of Bernd's proposal is that it makes sense from
a *practical* software reliability point of view.  It wouldn't hurt,
and might fix some significant bugs.  It's similar to the targets
which always implicitly clobber "cc".  It corresponds to what I always
assumed basic asm did, and I'm sure that I'm not alone.  This change
might fix some real bugs and it is extremely unlikely to break
anything.

Andrew.
Richard Biener May 9, 2016, 7:56 a.m. UTC | #6
On Thu, 5 May 2016, Bernd Edlinger wrote:

> Hi!
> 
> this patch is inspired by recent discussion about basic asm:
> 
> Currently a basic asm is an instruction scheduling barrier,
> but not a memory barrier, and most surprising, basic asm
> does _not_ implicitly clobber CC on targets where
> extended asm always implicitly clobbers CC, even if
> nothing is in the clobber section.
> 
> This patch makes basic asm implicitly clobber CC on certain
> targets, and makes the basic asm implicitly clobber memory,
> but no general registers, which is what could be expected.
> 
> This is however only done for basic asm with non-empty
> assembler string, which is in sync with David's proposed
> basic asm warnings patch.
> 
> Due to the change in the tree representation, where
> ASM_INPUT can now be the first element of a
> PARALLEL block with the implicit clobber elements,
> there are some changes necessary.
> 
> Most of the changes in the middle end, were necessary
> because extract_asm_operands can not be used to find out
> if a PARALLEL block is an asm statement, but in most cases
> asm_noperands can be used instead.
> 
> There are also changes necessary in two targets: pa, and ia64.
> I have successfully built cross-compilers for these targets.
> 
> Boot-strapped and reg-tested on x86_64-pc-linux-gnu
> OK for trunk?

I'm generally sympathetic with the change but I wonder if it would
make sense to re-write "basic asm" into general asms to not
need to special case them.  I'd do that during gimplification
for example.

At least it sounds to me that its semantics can be fully expressed
with generic asms?  (Maybe apart from the only-if-ASM_STRING-is-empty
part)

Thanks,
Richard.
Bernd Edlinger May 9, 2016, 1:37 p.m. UTC | #7
On 05/09/16 09:56, Richard Biener wrote:
> On Thu, 5 May 2016, Bernd Edlinger wrote:
>
>> Hi!
>>
>> this patch is inspired by recent discussion about basic asm:
>>
>> Currently a basic asm is an instruction scheduling barrier,
>> but not a memory barrier, and most surprising, basic asm
>> does _not_ implicitly clobber CC on targets where
>> extended asm always implicitly clobbers CC, even if
>> nothing is in the clobber section.
>>
>> This patch makes basic asm implicitly clobber CC on certain
>> targets, and makes the basic asm implicitly clobber memory,
>> but no general registers, which is what could be expected.
>>
>> This is however only done for basic asm with non-empty
>> assembler string, which is in sync with David's proposed
>> basic asm warnings patch.
>>
>> Due to the change in the tree representation, where
>> ASM_INPUT can now be the first element of a
>> PARALLEL block with the implicit clobber elements,
>> there are some changes necessary.
>>
>> Most of the changes in the middle end, were necessary
>> because extract_asm_operands can not be used to find out
>> if a PARALLEL block is an asm statement, but in most cases
>> asm_noperands can be used instead.
>>
>> There are also changes necessary in two targets: pa, and ia64.
>> I have successfully built cross-compilers for these targets.
>>
>> Boot-strapped and reg-tested on x86_64-pc-linux-gnu
>> OK for trunk?
>
> I'm generally sympathetic with the change but I wonder if it would
> make sense to re-write "basic asm" into general asms to not
> need to special case them.  I'd do that during gimplification
> for example.
>
> At least it sounds to me that its semantics can be fully expressed
> with generic asms?  (Maybe apart from the only-if-ASM_STRING-is-empty
> part)
>

That was also my first idea too.

In simple cases an asm ("whatever"); should do the same as
asm ("whatever" ::: );

Adding a "memory" to the clobber list would be simple that's true.

But in general it can be pretty complicated, especially if the
string contains the special characters % { | }.

For example asm ("#%") is OK, but asm ("#%" :) is an ERROR.
So, single % must be duplicated, that may be a general rule (hopefully).

On some targets { | } mean different things, and must be escaped,
but on other targets these must not be escaped.  So that is target
dependent.

Some targets replace whatever they want with the ASM_OUTPUT_OPCODE hook.
Example: i386 replaces "%v" with "v" or "", tilegx replaces "pseudo"
with "", this hook is only called with extended asm.  And we must know
what it does and reverse it's effect.  Here it starts to become
difficult.

And the ia64 target have different semantics for basic asm than without,
i.e. they always emit stop bits for traditional asms.  So they
make a difference between extended and basic asm.

I have really no idea how to do this when extended and basic asm have
exactly the same tree structure.

That's why I thought I can as well add the clobbers to the basic asm's
tree representation.



Thanks
Bernd.

> Thanks,
> Richard.
>
Bernd Schmidt May 9, 2016, 1:46 p.m. UTC | #8
On 05/09/2016 03:37 PM, Bernd Edlinger wrote:
> On 05/09/16 09:56, Richard Biener wrote:
>>
>> At least it sounds to me that its semantics can be fully expressed
>> with generic asms?  (Maybe apart from the only-if-ASM_STRING-is-empty
>> part)
>>
>
> That was also my first idea too.
>
> In simple cases an asm ("whatever"); should do the same as
> asm ("whatever" ::: );
>
> Adding a "memory" to the clobber list would be simple that's true.
>
> But in general it can be pretty complicated, especially if the
> string contains the special characters % { | }.

Is the only difference in how the string is output? Maybe we can have a 
slightly different form of ASM_OPERANDS (with a bit set, or with the 
string wrapped in something else) to indicate that it's old-style.


Bernd
Bernd Edlinger May 9, 2016, 9:12 p.m. UTC | #9
On 05/09/16 15:46, Bernd Schmidt wrote:
> On 05/09/2016 03:37 PM, Bernd Edlinger wrote:
>> On 05/09/16 09:56, Richard Biener wrote:
>>>
>>> At least it sounds to me that its semantics can be fully expressed
>>> with generic asms?  (Maybe apart from the only-if-ASM_STRING-is-empty
>>> part)
>>>
>>
>> That was also my first idea too.
>>
>> In simple cases an asm ("whatever"); should do the same as
>> asm ("whatever" ::: );
>>
>> Adding a "memory" to the clobber list would be simple that's true.
>>
>> But in general it can be pretty complicated, especially if the
>> string contains the special characters % { | }.
>
> Is the only difference in how the string is output? Maybe we can have a
> slightly different form of ASM_OPERANDS (with a bit set, or with the
> string wrapped in something else) to indicate that it's old-style.

Most of the difference is what happens in final.c, and adding a new
attribute to the ASM_OPERANDS tree node is definitely one option.
I tried to implement it in a way that causes the least confusion.

There are lots of different tree representations for an extended asm
statement in genereal, but only one for a basic asm.

An extended asm that has no outputs and no clobbers, is an ASM_OPERAND
node with optional vector of ASM_INPUTs containig the input constraint:

ASM_OPERAND { "asm", "", 0, VEC { inputs...}, VEC { ASM_INPUT ("x")...}

but if it has any CLOBBERS, it will look like this:

PARALLEL { ASM_OPERAND, CLOBBER... }

if it has one output, and zero clobbers we have:

SET { x, ASM_OPERAND }

and in case we have more than one output we have:

PARALLEL { SET { x, ASM_OPERAND }... , CLOBBER... }


A basic asm is just an ASM_INPUT that is not underneath an ASM_OPERAND.

But to add any CLOBBERs to this ASM_INPUT it has to be in PARALLEL
with the CLOBBERs, so that would look like this:

PARALLEL { ASM_INPUT{ "asm" }, CLOBBER... }


There are lots of places where we need to know if a statement is an
assembler statement, in most places this is done in this way:

GET_CODE (PATTERN (insn)) == ASM_INPUT
|| asm_noperands (PATTERN (insn)) >= 0

There are a handful of places where it is done it this way:

GET_CODE (PATTERN (insn)) == ASM_INPUT
|| extract_asm_operands (PATTERN (insn)) != NULL_RTX

extract_asm_operands locates the ASM_OPERAND node from an extended
asm that can have either of the several forms above, but in most
cases the result is not looked at.  Making extract_asm_operands
return anything but an ASM_OPERANDS is impossible, but making
asm_noperands return 0 for a PARALLEL { ASM_INPUT, CLOBBER... }
is not too complicated.

Fortunately, all the remaining uses of extract_asm_operands really
mean an extended asm.

Hope that explains my idea.


Thanks
Bernd.
Jeff Law May 18, 2016, 10:07 p.m. UTC | #10
On 05/07/2016 11:38 AM, Andrew Haley wrote:
> On 06/05/16 07:35, David Wohlferd wrote:
>
>> 1) I'm not clear precisely what problem this patch fixes.  It's true
>> that some people have incorrectly assumed that basic asm clobbers
>> memory and this change would fix their code.  But some people also
>> incorrectly assume it clobbers registers.  I assume that's why Jeff
>> Law proposed making basic asm "an opaque blob that
>> read/write/clobber any register or memory location."
>
> A few more things:
>
> Jeff Law did propose this, but it's impossible to do because it
> inevitably causes reload failures.
Right.

>
> My argument in support of Bernd's proposal is that it makes sense from
> a *practical* software reliability point of view.  It wouldn't hurt,
> and might fix some significant bugs.  It's similar to the targets
> which always implicitly clobber "cc".  It corresponds to what I always
> assumed basic asm did, and I'm sure that I'm not alone.  This change
> might fix some real bugs and it is extremely unlikely to break
> anything.
And by making basic asms use/clobber memory in particular, it means code 
using them is less likely to break as the optimizers continue to get 
smarter about memory loads/stores.

I haven't gone through the actual patch yet, but I like it's basic goals.

Jeff
David Wohlferd May 20, 2016, 6:50 a.m. UTC | #11
On 5/18/2016 3:07 PM, Jeff Law wrote:
> On 05/07/2016 11:38 AM, Andrew Haley wrote:
>> My argument in support of Bernd's proposal is that it makes sense from
>> a *practical* software reliability point of view.  It wouldn't hurt,
>> and might fix some significant bugs.  It's similar to the targets
>> which always implicitly clobber "cc".  It corresponds to what I always
>> assumed basic asm did, and I'm sure that I'm not alone.  This change
>> might fix some real bugs and it is extremely unlikely to break
>> anything.
> And by making basic asms use/clobber memory in particular, it means 
> code using them is less likely to break as the optimizers continue to 
> get smarter about memory loads/stores.
>
> I haven't gone through the actual patch yet, but I like it's basic goals.

Other than the doc changes I already mentioned, I have no technical 
issues with the patch (although I don't speak 'internals' well enough to 
approve it either).

That said, I'd like to make one final pitch for the alternate solution 
proposed by Richard Henderson:

    "deprecate and later completely remove basic asm within functions"

IOW: Basic asm can only be used at "top level."  Within functions, 
extended asm must be used.

The reason I asked for clarification on what problem this patch is 
intended to solve, is that basic asm has a number of them.  Given it:

- Doesn't clobber memory
- Doesn't clobber used registers
- Doesn't clobber flags
- Doesn't have dependencies

This makes gcc's basic asm:

- incompatible with user expectations.
- incompatible with other compilers.
- difficult for optimizers to correctly and consistently position.

Bernd's patch adding the memory clobber does help with all of these.  
And I agree that it is unlikely to cause the generation of incorrect 
code.  However unlike Andrew, I'm not prepared to say it doesn't 'hurt.'

At a minimum, suddenly forcing an unexpected/unneeded memory clobber can 
adversely impact the optimization of surrounding code.  This can be 
particularly annoying if the reason for the asm was to improve 
performance.  And adding a memory clobber does add a dependency of 
sorts, which might cause the location of the asm to shift in an 
unfortunate way.  And there's always the long-shot possibility that some 
weird quirk or (very) badly-written code will cause the asm to flat out 
fail when used with a memory clobber.  And if this change does produce 
any of these problems, I feel pity for whoever has to track it down.

But the real problem I have with this patch is that it doesn't actually 
solve the problem of user expectations.  Or compatibility with other 
compilers.  Or the problem with positioning.  In fact this change makes 
the problem with expectations worse for the people who are knowledgeable 
about the current decades-old behavior.

Ultimately, the design of gcc makes these problems inherently 
unsolvable.  That's why I believe deprecation/removal is the real 
solution here.

I realize deprecation/removal is drastic.  Especially since basic asm 
(mostly) works as is.  But fixing memory clobbers while leaving the rest 
broken feels like half a solution, meaning that some day we're going to 
have to fiddle with this again.  If we are going to spend the time and 
effort (both ours and our users') to address basic-asm-in-a-function's 
problems, let's make sure we really solve them.

dw
Andrew Haley May 22, 2016, 10:33 a.m. UTC | #12
On 05/20/2016 07:50 AM, David Wohlferd wrote:

> At a minimum, suddenly forcing an unexpected/unneeded memory clobber
> can adversely impact the optimization of surrounding code.  This can
> be particularly annoying if the reason for the asm was to improve
> performance.  And adding a memory clobber does add a dependency of
> sorts, which might cause the location of the asm to shift in an
> unfortunate way.  And there's always the long-shot possibility that
> some weird quirk or (very) badly-written code will cause the asm to
> flat out fail when used with a memory clobber.  And if this change
> does produce any of these problems, I feel pity for whoever has to
> track it down.

OTOH, if a memory clobber does change code gen it probably changes it
in a way which better fits user expectations, and perhaps it fixes a
bug.  That's a win, and it is far, far more important than any other
consideration.

Given that a basic asm statements has neither inputs nor outputs, it
must have side effects to be useful.  All this patch does is recognize
that fact.  I'm not saying your scenario won't occur, but it won't in
the majority of cases.

> I realize deprecation/removal is drastic.  Especially since basic
> asm (mostly) works as is.  But fixing memory clobbers while leaving
> the rest broken feels like half a solution, meaning that some day
> we're going to have to fiddle with this again.

Yes, we will undoubtedly have to fiddle with basic asm again.  We
should plan for deprecation.

But I think you're close to the all-or-nothing fallacy: that because
this patch doesn't solve all the problems with basic asm, it isn't
worth having.

Andrew.
Richard Biener May 23, 2016, 7:46 a.m. UTC | #13
On Sun, 22 May 2016, Andrew Haley wrote:

> On 05/20/2016 07:50 AM, David Wohlferd wrote:
> 
> > At a minimum, suddenly forcing an unexpected/unneeded memory clobber
> > can adversely impact the optimization of surrounding code.  This can
> > be particularly annoying if the reason for the asm was to improve
> > performance.  And adding a memory clobber does add a dependency of
> > sorts, which might cause the location of the asm to shift in an
> > unfortunate way.  And there's always the long-shot possibility that
> > some weird quirk or (very) badly-written code will cause the asm to
> > flat out fail when used with a memory clobber.  And if this change
> > does produce any of these problems, I feel pity for whoever has to
> > track it down.
> 
> OTOH, if a memory clobber does change code gen it probably changes it
> in a way which better fits user expectations, and perhaps it fixes a
> bug.  That's a win, and it is far, far more important than any other
> consideration.
> 
> Given that a basic asm statements has neither inputs nor outputs, it
> must have side effects to be useful.  All this patch does is recognize
> that fact.  I'm not saying your scenario won't occur, but it won't in
> the majority of cases.
> 
> > I realize deprecation/removal is drastic.  Especially since basic
> > asm (mostly) works as is.  But fixing memory clobbers while leaving
> > the rest broken feels like half a solution, meaning that some day
> > we're going to have to fiddle with this again.
> 
> Yes, we will undoubtedly have to fiddle with basic asm again.  We
> should plan for deprecation.
> 
> But I think you're close to the all-or-nothing fallacy: that because
> this patch doesn't solve all the problems with basic asm, it isn't
> worth having.

I think adding memory clobbers is worth having.  I also think that
deprecating basic asms would be a good thing, so can we please
add a new warning for that?  "warning: basic asms are deprecated"

Richard.
Jeff Law May 23, 2016, 3:58 p.m. UTC | #14
On 05/22/2016 04:33 AM, Andrew Haley wrote:
> On 05/20/2016 07:50 AM, David Wohlferd wrote:
>
>> At a minimum, suddenly forcing an unexpected/unneeded memory clobber
>> can adversely impact the optimization of surrounding code.  This can
>> be particularly annoying if the reason for the asm was to improve
>> performance.  And adding a memory clobber does add a dependency of
>> sorts, which might cause the location of the asm to shift in an
>> unfortunate way.  And there's always the long-shot possibility that
>> some weird quirk or (very) badly-written code will cause the asm to
>> flat out fail when used with a memory clobber.  And if this change
>> does produce any of these problems, I feel pity for whoever has to
>> track it down.
>
> OTOH, if a memory clobber does change code gen it probably changes it
> in a way which better fits user expectations, and perhaps it fixes a
> bug.  That's a win, and it is far, far more important than any other
> consideration.
My thoughts precisely.

>
>> I realize deprecation/removal is drastic.  Especially since basic
>> asm (mostly) works as is.  But fixing memory clobbers while leaving
>> the rest broken feels like half a solution, meaning that some day
>> we're going to have to fiddle with this again.
>
> Yes, we will undoubtedly have to fiddle with basic asm again.  We
> should plan for deprecation.
Right.  There are some fundamental problems with basic asms and I think 
we want to deprecate them in the long term.  In the immediate/medium 
term, I think addressing the memory dependency issue is the right thing 
to do.

While it may make some code somewhere less optimized, it brings the 
basic asm semantics closer to what most programmers expect and prevents 
them from suddenly breaking as the optimizers continue to improve.  If 
someone wants better optimized code, they ought to be using extended 
asms anyway.

Jeff
David Wohlferd May 23, 2016, 9:46 p.m. UTC | #15
On 5/23/2016 12:46 AM, Richard Biener wrote:
 > On Sun, 22 May 2016, Andrew Haley wrote:
 >> On 05/20/2016 07:50 AM, David Wohlferd wrote:
 >>> I realize deprecation/removal is drastic.  Especially since basic
 >>> asm (mostly) works as is.  But fixing memory clobbers while leaving
 >>> the rest broken feels like half a solution, meaning that some day
 >>> we're going to have to fiddle with this again.
 >>
 >> Yes, we will undoubtedly have to fiddle with basic asm again.  We
 >> should plan for deprecation.
 >
 > I think adding memory clobbers is worth having.  I also think that
 > deprecating basic asms would be a good thing, so can we please
 > add a new warning for that?  "warning: basic asms are deprecated"

I've still got the -Wbasic-asm patch where I proposed this for v6. I can 
dust it off again and re-submit it.  A couple questions first:

1) In this patch the warning was disabled by default.  But it sounds 
like you want it enabled by default?  Easy to change, I'm just 
confirming your intent.

2) Is 'deprecated' handled differently than other types of warnings?  
There is a -Wno-deprecated, but it seems to have a very specific meaning 
that does not apply here.

3) The warning text in the old patch was "asm statement in function does 
not use extended syntax".  The intent was:

a) Don't make it sound like basic asm is completely gone, since it can 
still be used at top level.
b) Don't make it sound like all inline asm is gone, since extended asm 
can still be used in functions.
c) Convey all that in as few words as possible.

Now that we want to add the word 'deprecated,' perhaps one of these:

- Basic asm in functions is deprecated in favor of extended syntax
- asm in functions without extended syntax is deprecated
- Deprecated: basic asm in function
- Deprecated: asm in function without extended syntax

I like the last one (people may not know what 'basic' means in this 
context), but any of these would work for me.  Preferences?

In order to avoid conflicts, I'll wait for Bernd to commit his patch first.

dw
diff mbox

Patch

Index: gcc/cfgexpand.c
===================================================================
--- gcc/cfgexpand.c	(revision 231412)
+++ gcc/cfgexpand.c	(working copy)
@@ -2655,9 +2655,6 @@  expand_asm_loc (tree string, int vol, location_t l
 {
   rtx body;
 
-  if (TREE_CODE (string) == ADDR_EXPR)
-    string = TREE_OPERAND (string, 0);
-
   body = gen_rtx_ASM_INPUT_loc (VOIDmode,
 				ggc_strdup (TREE_STRING_POINTER (string)),
 				locus);
@@ -2664,6 +2661,34 @@  expand_asm_loc (tree string, int vol, location_t l
 
   MEM_VOLATILE_P (body) = vol;
 
+  /* Non-empty basic ASM implicitly clobbers memory.  */
+  if (TREE_STRING_LENGTH (string) != 0)
+    {
+      rtx asm_op, clob;
+      unsigned i, nclobbers;
+      auto_vec<rtx> input_rvec, output_rvec;
+      auto_vec<const char *> constraints;
+      auto_vec<rtx> clobber_rvec;
+      HARD_REG_SET clobbered_regs;
+      CLEAR_HARD_REG_SET (clobbered_regs);
+
+      clob = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+      clobber_rvec.safe_push (clob);
+
+      if (targetm.md_asm_adjust)
+	targetm.md_asm_adjust (output_rvec, input_rvec,
+			       constraints, clobber_rvec,
+			       clobbered_regs);
+
+      asm_op = body;
+      nclobbers = clobber_rvec.length ();
+      body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (1 + nclobbers));
+
+      XVECEXP (body, 0, 0) = asm_op;
+      for (i = 0; i < nclobbers; i++)
+	XVECEXP (body, 0, i + 1) = gen_rtx_CLOBBER (VOIDmode, clobber_rvec[i]);
+    }
+
   emit_insn (body);
 }
 
Index: gcc/compare-elim.c
===================================================================
--- gcc/compare-elim.c	(revision 231412)
+++ gcc/compare-elim.c	(working copy)
@@ -162,7 +162,7 @@  arithmetic_flags_clobber_p (rtx_insn *insn)
   if (!NONJUMP_INSN_P (insn))
     return false;
   pat = PATTERN (insn);
-  if (extract_asm_operands (pat))
+  if (asm_noperands (pat) >= 0)
     return false;
 
   if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) == 2)
Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 231412)
+++ gcc/doc/extend.texi	(working copy)
@@ -7442,6 +7442,10 @@  all basic @code{asm} blocks use the assembler dial
 Basic @code{asm} provides no
 mechanism to provide different assembler strings for different dialects.
 
+For basic @code{asm} with non-empty assembler string GCC assumes
+the assembler block does not change any general purpose registers,
+but it may read or write any globally accessible variable.
+
 Here is an example of basic @code{asm} for i386:
 
 @example
Index: gcc/final.c
===================================================================
--- gcc/final.c	(revision 231412)
+++ gcc/final.c	(working copy)
@@ -2565,6 +2565,10 @@  final_scan_insn (rtx_insn *insn, FILE *file, int o
 	  (*debug_hooks->source_line) (last_linenum, last_filename,
 				       last_discriminator, is_stmt);
 
+	if (GET_CODE (body) == PARALLEL
+	    && GET_CODE (XVECEXP (body, 0, 0)) == ASM_INPUT)
+	  body = XVECEXP (body, 0, 0);
+
 	if (GET_CODE (body) == ASM_INPUT)
 	  {
 	    const char *string = XSTR (body, 0);
Index: gcc/gimple.c
===================================================================
--- gcc/gimple.c	(revision 231412)
+++ gcc/gimple.c	(working copy)
@@ -2567,6 +2567,10 @@  gimple_asm_clobbers_memory_p (const gasm *stmt)
 	return true;
     }
 
+  /* Non-empty basic ASM implicitly clobbers memory.  */
+  if (gimple_asm_input_p (stmt) && strlen (gimple_asm_string (stmt)) != 0)
+    return true;
+
   return false;
 }
 
Index: gcc/ira.c
===================================================================
--- gcc/ira.c	(revision 231412)
+++ gcc/ira.c	(working copy)
@@ -2229,7 +2229,7 @@  compute_regs_asm_clobbered (void)
 	{
 	  df_ref def;
 
-	  if (NONDEBUG_INSN_P (insn) && extract_asm_operands (PATTERN (insn)))
+	  if (NONDEBUG_INSN_P (insn) && asm_noperands (PATTERN (insn)) >= 0)
 	    FOR_EACH_INSN_DEF (def, insn)
 	      {
 		unsigned int dregno = DF_REF_REGNO (def);
Index: gcc/recog.c
===================================================================
--- gcc/recog.c	(revision 231412)
+++ gcc/recog.c	(working copy)
@@ -1470,6 +1470,8 @@  extract_asm_operands (rtx body)
 
 /* If BODY is an insn body that uses ASM_OPERANDS,
    return the number of operands (both input and output) in the insn.
+   If BODY is an insn body that uses ASM_INPUT with CLOBBERS in PARALLEL,
+   return 0.
    Otherwise return -1.  */
 
 int
@@ -1476,16 +1478,26 @@  int
 asm_noperands (const_rtx body)
 {
   rtx asm_op = extract_asm_operands (CONST_CAST_RTX (body));
-  int n_sets = 0;
+  int i, n_sets = 0;
 
   if (asm_op == NULL)
-    return -1;
+    {
+      if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) >= 2
+	  && GET_CODE (XVECEXP (body, 0, 0)) == ASM_INPUT)
+	{
+	  /* body is [(asm_input ...) (clobber (reg ...))...].  */
+	  for (i = XVECLEN (body, 0) - 1; i > 0; i--)
+	    if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
+	      return -1;
+	  return 0;
+	}
+      return -1;
+    }
 
   if (GET_CODE (body) == SET)
     n_sets = 1;
   else if (GET_CODE (body) == PARALLEL)
     {
-      int i;
       if (GET_CODE (XVECEXP (body, 0, 0)) == SET)
 	{
 	  /* Multiple output operands, or 1 output plus some clobbers:
@@ -1540,9 +1552,12 @@  asm_noperands (const_rtx body)
    the locations of the operands within the insn into the vector OPERAND_LOCS,
    and the constraints for the operands into CONSTRAINTS.
    Write the modes of the operands into MODES.
+   Write the location info into LOC.
    Return the assembler-template.
+   If BODY is an insn body that uses ASM_INPUT with CLOBBERS in PARALLEL,
+   return the basic assembly string.
 
-   If MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0,
+   If LOC, MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0,
    we don't store that info.  */
 
 const char *
@@ -1603,6 +1618,12 @@  decode_asm_operands (rtx body, rtx *operands, rtx
 	      }
 	    nbase = i;
 	  }
+	else if (GET_CODE (asmop) == ASM_INPUT)
+	  {
+	    if (loc)
+	      *loc = ASM_INPUT_SOURCE_LOCATION (asmop);
+	    return XSTR (asmop, 0);
+	  }
 	break;
       }
 
@@ -2244,7 +2265,8 @@  extract_insn (rtx_insn *insn)
     case PARALLEL:
       if ((GET_CODE (XVECEXP (body, 0, 0)) == SET
 	   && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
-	  || GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+	  || GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS
+	  || GET_CODE (XVECEXP (body, 0, 0)) == ASM_INPUT)
 	goto asm_insn;
       else
 	goto normal_insn;
Index: gcc/testsuite/gcc.target/i386/pr24414.c
===================================================================
--- gcc/testsuite/gcc.target/i386/pr24414.c	(revision 0)
+++ gcc/testsuite/gcc.target/i386/pr24414.c	(working copy)
@@ -0,0 +1,13 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+int test;
+
+int
+main ()
+{
+  int x = test;
+  asm ("movl $1,test");
+  if (x + test != 1)
+    __builtin_trap ();
+  return 0;
+}
Index: gcc/config/ia64/ia64.c
===================================================================
--- gcc/config/ia64/ia64.c	(revision 231412)
+++ gcc/config/ia64/ia64.c	(working copy)
@@ -6549,6 +6549,7 @@  rtx_needs_barrier (rtx x, struct reg_flags flags,
 	    case USE:
 	    case CALL:
 	    case ASM_OPERANDS:
+	    case ASM_INPUT:
 	      need_barrier |= rtx_needs_barrier (pat, flags, pred);
 	      break;
 
Index: gcc/config/pa/pa.c
===================================================================
--- gcc/config/pa/pa.c	(revision 231412)
+++ gcc/config/pa/pa.c	(working copy)
@@ -6399,7 +6399,7 @@  branch_to_delay_slot_p (rtx_insn *insn)
 	 the branch is followed by an asm.  */
       if (!insn
 	  || GET_CODE (PATTERN (insn)) == ASM_INPUT
-	  || extract_asm_operands (PATTERN (insn)) != NULL_RTX
+	  || asm_noperands (PATTERN (insn)) >= 0
 	  || get_attr_length (insn) > 0)
 	break;
     }
@@ -6430,7 +6430,7 @@  branch_needs_nop_p (rtx_insn *insn)
 	return TRUE;
 
       if (!(GET_CODE (PATTERN (insn)) == ASM_INPUT
-	   || extract_asm_operands (PATTERN (insn)) != NULL_RTX)
+	   || asm_noperands (PATTERN (insn)) >= 0)
 	  && get_attr_length (insn) > 0)
 	break;
     }
@@ -6454,7 +6454,7 @@  use_skip_p (rtx_insn *insn)
       /* We can't rely on the length of asms, so we can't skip asms.  */
       if (!insn
 	  || GET_CODE (PATTERN (insn)) == ASM_INPUT
-	  || extract_asm_operands (PATTERN (insn)) != NULL_RTX)
+	  || asm_noperands (PATTERN (insn)) >= 0)
 	break;
       if (get_attr_length (insn) == 4
 	  && jump_insn == next_active_insn (insn))