audk/MdePkg/Library/BaseSynchronizationLib/Ia32/GccInline.c

202 lines
5.6 KiB
C
Raw Normal View History

/** @file
GCC inline implementation of BaseSynchronizationLib processor specific functions.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
/**
Performs an atomic increment of an 32-bit unsigned integer.
Performs an atomic increment of the 32-bit unsigned integer specified by
Value and returns the incremented value. The increment operation must be
performed using MP safe mechanisms.
@param Value A pointer to the 32-bit value to increment.
@return The incremented value.
**/
UINT32
EFIAPI
InternalSyncIncrement (
IN volatile UINT32 *Value
)
{
UINT32 Result;
__asm__ __volatile__ (
"movl $1, %%eax \n\t"
"lock \n\t"
MdePkg/BaseSynchronizationLib: fix XADD operands in GCC IA32/X64 assembly Currently, "gcc-4.8.5-28.el7_5.1.x86_64" generates the following code for me, from the XADD inline assembly added to "X64/GccInline.c" in commit 17634d026f96: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 00 lock xadd %eax,(%rax) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : "m" (*Value) // %2 > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq > The MOV $0X1,%EAX instruction corrupts the address of Value in %RAX before we reach the XADD instruction. In fact, it makes no sense for XADD to use %EAX as source operand and (%RAX) as destination operand at the same time. The XADD instruction's destination operand is a read-write operand. The GCC documentation states: > The ordinary output operands must be write-only; GCC will assume that > the values in these operands before the instruction are dead and need > not be generated. Extended asm supports input-output or read-write > operands. Use the constraint character `+' to indicate such an operand > and list it with the output operands. You should only use read-write > operands when the constraints for the operand (or the operand in which > only some of the bits are to be changed) allow a register. (The above is intentionally quoted from the oldest GCC release that edk2 supports, namely gcc-4.4: <https://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Extended-Asm.html>.) Fix the operand list accordingly. With the patch applied, I get: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 02 lock xadd %eax,(%rdx) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : // no inputs that aren't also outputs > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq Note that some other bugs remain in "BaseSynchronizationLib/*/GccInline.c"; those should be addressed later, under <https://bugzilla.tianocore.org/show_bug.cgi?id=1208>. Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Ruiyu Ni <ruiyu.ni@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1207 Fixes: 17634d026f968c404b039a8d8431b6389dd396ea Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
2018-09-25 16:58:15 +02:00
"xadd %%eax, %1 \n\t"
"inc %%eax \n\t"
MdePkg/BaseSynchronizationLib: Fix InternalSync[De|In]crement REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1303 Today's code generates assembly code as below for InternalSyncIncrement: __asm__ __volatile__ ( "movl $1, %%eax \n\t" "lock \n\t" "xadd %%eax, %1 \n\t" "inc %%eax \n\t" : "=a" (Result), // %0 "+m" (*Value) // %1 : // no inputs that aren't also outputs : "memory", "cc" ); 0: 55 pushl %ebp 1: 89 e5 movl %esp, %ebp 3: 8b 45 08 movl 8(%ebp), %eax 6: b8 01 00 00 00 movl $1, %eax b: f0 lock c: 0f c1 00 xaddl %eax, _InternalSyncIncrement(%eax) f: 40 incl %eax 10: 5d popl %ebp 11: c3 retl Line #3 and Line #6 both use EAX as destination register. Line #c uses EAX and (EAX). The output operand "=a" tells GCC that EAX is used for output. But GCC only assumes that EAX will be used in the very last instruction. Per GCC document, "Use the '&' constraint modifier on all output operands that must not overlap an input. Otherwise, GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction." "=&a" should be used to tell GCC not use EAX before the assembly. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Laszlo Ersek <lersek@redhat.com> Suggested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Fixes: 8a94eb9283fa09a30f5f06f0c12cf0ee4e14fbcf Fixes: 17634d026f968c404b039a8d8431b6389dd396ea
2018-11-07 16:51:51 +01:00
: "=&a" (Result), // %0
MdePkg/BaseSynchronizationLib: fix XADD operands in GCC IA32/X64 assembly Currently, "gcc-4.8.5-28.el7_5.1.x86_64" generates the following code for me, from the XADD inline assembly added to "X64/GccInline.c" in commit 17634d026f96: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 00 lock xadd %eax,(%rax) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : "m" (*Value) // %2 > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq > The MOV $0X1,%EAX instruction corrupts the address of Value in %RAX before we reach the XADD instruction. In fact, it makes no sense for XADD to use %EAX as source operand and (%RAX) as destination operand at the same time. The XADD instruction's destination operand is a read-write operand. The GCC documentation states: > The ordinary output operands must be write-only; GCC will assume that > the values in these operands before the instruction are dead and need > not be generated. Extended asm supports input-output or read-write > operands. Use the constraint character `+' to indicate such an operand > and list it with the output operands. You should only use read-write > operands when the constraints for the operand (or the operand in which > only some of the bits are to be changed) allow a register. (The above is intentionally quoted from the oldest GCC release that edk2 supports, namely gcc-4.4: <https://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Extended-Asm.html>.) Fix the operand list accordingly. With the patch applied, I get: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 02 lock xadd %eax,(%rdx) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : // no inputs that aren't also outputs > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq Note that some other bugs remain in "BaseSynchronizationLib/*/GccInline.c"; those should be addressed later, under <https://bugzilla.tianocore.org/show_bug.cgi?id=1208>. Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Ruiyu Ni <ruiyu.ni@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1207 Fixes: 17634d026f968c404b039a8d8431b6389dd396ea Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
2018-09-25 16:58:15 +02:00
"+m" (*Value) // %1
: // no inputs that aren't also outputs
: "memory",
"cc"
);
return Result;
}
/**
Performs an atomic decrement of an 32-bit unsigned integer.
Performs an atomic decrement of the 32-bit unsigned integer specified by
Value and returns the decremented value. The decrement operation must be
performed using MP safe mechanisms.
@param Value A pointer to the 32-bit value to decrement.
@return The decremented value.
**/
UINT32
EFIAPI
InternalSyncDecrement (
IN volatile UINT32 *Value
)
{
UINT32 Result;
__asm__ __volatile__ (
"movl $-1, %%eax \n\t"
"lock \n\t"
MdePkg/BaseSynchronizationLib: fix XADD operands in GCC IA32/X64 assembly Currently, "gcc-4.8.5-28.el7_5.1.x86_64" generates the following code for me, from the XADD inline assembly added to "X64/GccInline.c" in commit 17634d026f96: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 00 lock xadd %eax,(%rax) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : "m" (*Value) // %2 > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq > The MOV $0X1,%EAX instruction corrupts the address of Value in %RAX before we reach the XADD instruction. In fact, it makes no sense for XADD to use %EAX as source operand and (%RAX) as destination operand at the same time. The XADD instruction's destination operand is a read-write operand. The GCC documentation states: > The ordinary output operands must be write-only; GCC will assume that > the values in these operands before the instruction are dead and need > not be generated. Extended asm supports input-output or read-write > operands. Use the constraint character `+' to indicate such an operand > and list it with the output operands. You should only use read-write > operands when the constraints for the operand (or the operand in which > only some of the bits are to be changed) allow a register. (The above is intentionally quoted from the oldest GCC release that edk2 supports, namely gcc-4.4: <https://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Extended-Asm.html>.) Fix the operand list accordingly. With the patch applied, I get: > 0000000000004383 <InternalSyncIncrement>: > UINT32 > EFIAPI > InternalSyncIncrement ( > IN volatile UINT32 *Value > ) > { > 4383: 55 push %rbp > 4384: 48 89 e5 mov %rsp,%rbp > 4387: 48 83 ec 10 sub $0x10,%rsp > 438b: 48 89 4d 10 mov %rcx,0x10(%rbp) > UINT32 Result; > > __asm__ __volatile__ ( > 438f: 48 8b 55 10 mov 0x10(%rbp),%rdx > 4393: 48 8b 45 10 mov 0x10(%rbp),%rax > 4397: b8 01 00 00 00 mov $0x1,%eax > 439c: f0 0f c1 02 lock xadd %eax,(%rdx) > 43a0: ff c0 inc %eax > 43a2: 89 45 fc mov %eax,-0x4(%rbp) > : // no inputs that aren't also outputs > : "memory", > "cc" > ); > > return Result; > 43a5: 8b 45 fc mov -0x4(%rbp),%eax > } > 43a8: c9 leaveq > 43a9: c3 retq Note that some other bugs remain in "BaseSynchronizationLib/*/GccInline.c"; those should be addressed later, under <https://bugzilla.tianocore.org/show_bug.cgi?id=1208>. Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Ruiyu Ni <ruiyu.ni@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1207 Fixes: 17634d026f968c404b039a8d8431b6389dd396ea Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
2018-09-25 16:58:15 +02:00
"xadd %%eax, %1 \n\t"
"dec %%eax \n\t"
MdePkg/BaseSynchronizationLib: Fix InternalSync[De|In]crement REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1303 Today's code generates assembly code as below for InternalSyncIncrement: __asm__ __volatile__ ( "movl $1, %%eax \n\t" "lock \n\t" "xadd %%eax, %1 \n\t" "inc %%eax \n\t" : "=a" (Result), // %0 "+m" (*Value) // %1 : // no inputs that aren't also outputs : "memory", "cc" ); 0: 55 pushl %ebp 1: 89 e5 movl %esp, %ebp 3: 8b 45 08 movl 8(%ebp), %eax 6: b8 01 00 00 00 movl $1, %eax b: f0 lock c: 0f c1 00 xaddl %eax, _InternalSyncIncrement(%eax) f: 40 incl %eax 10: 5d popl %ebp 11: c3 retl Line #3 and Line #6 both use EAX as destination register. Line #c uses EAX and (EAX). The output operand "=a" tells GCC that EAX is used for output. But GCC only assumes that EAX will be used in the very last instruction. Per GCC document, "Use the '&' constraint modifier on all output operands that must not overlap an input. Otherwise, GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction." "=&a" should be used to tell GCC not use EAX before the assembly. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Laszlo Ersek <lersek@redhat.com> Suggested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Fixes: 8a94eb9283fa09a30f5f06f0c12cf0ee4e14fbcf Fixes: 17634d026f968c404b039a8d8431b6389dd396ea
2018-11-07 16:51:51 +01:00
: "=&a" (Result), // %0
"+m" (*Value) // %1
: // no inputs that aren't also outputs
: "memory",
"cc"
);
return Result;
}
/**
Performs an atomic compare exchange operation on a 16-bit unsigned integer.
Performs an atomic compare exchange operation on the 16-bit unsigned integer
specified by Value. If Value is equal to CompareValue, then Value is set to
ExchangeValue and CompareValue is returned. If Value is not equal to CompareValue,
then Value is returned. The compare exchange operation must be performed using
MP safe mechanisms.
@param Value A pointer to the 16-bit value for the compare exchange
operation.
@param CompareValue 16-bit value used in compare operation.
@param ExchangeValue 16-bit value used in exchange operation.
@return The original *Value before exchange.
**/
UINT16
EFIAPI
InternalSyncCompareExchange16 (
IN OUT volatile UINT16 *Value,
IN UINT16 CompareValue,
IN UINT16 ExchangeValue
)
{
__asm__ __volatile__ (
"lock \n\t"
"cmpxchgw %2, %1 \n\t"
: "+a" (CompareValue), // %0
"+m" (*Value) // %1
: "q" (ExchangeValue) // %2
: "memory",
"cc"
);
return CompareValue;
}
/**
Performs an atomic compare exchange operation on a 32-bit unsigned integer.
Performs an atomic compare exchange operation on the 32-bit unsigned integer
specified by Value. If Value is equal to CompareValue, then Value is set to
ExchangeValue and CompareValue is returned. If Value is not equal to CompareValue,
then Value is returned. The compare exchange operation must be performed using
MP safe mechanisms.
@param Value A pointer to the 32-bit value for the compare exchange
operation.
@param CompareValue 32-bit value used in compare operation.
@param ExchangeValue 32-bit value used in exchange operation.
@return The original *Value before exchange.
**/
UINT32
EFIAPI
InternalSyncCompareExchange32 (
IN OUT volatile UINT32 *Value,
IN UINT32 CompareValue,
IN UINT32 ExchangeValue
)
{
__asm__ __volatile__ (
"lock \n\t"
"cmpxchgl %2, %1 \n\t"
: "+a" (CompareValue), // %0
"+m" (*Value) // %1
: "q" (ExchangeValue) // %2
: "memory",
"cc"
);
return CompareValue;
}
/**
Performs an atomic compare exchange operation on a 64-bit unsigned integer.
Performs an atomic compare exchange operation on the 64-bit unsigned integer specified
by Value. If Value is equal to CompareValue, then Value is set to ExchangeValue and
CompareValue is returned. If Value is not equal to CompareValue, then Value is returned.
The compare exchange operation must be performed using MP safe mechanisms.
@param Value A pointer to the 64-bit value for the compare exchange
operation.
@param CompareValue 64-bit value used in compare operation.
@param ExchangeValue 64-bit value used in exchange operation.
@return The original *Value before exchange.
**/
UINT64
EFIAPI
InternalSyncCompareExchange64 (
IN OUT volatile UINT64 *Value,
IN UINT64 CompareValue,
IN UINT64 ExchangeValue
)
{
__asm__ __volatile__ (
"lock \n\t"
"cmpxchg8b (%1) \n\t"
: "+A" (CompareValue) // %0
: "S" (Value), // %1
"b" ((UINT32) ExchangeValue), // %2
"c" ((UINT32) (ExchangeValue >> 32)) // %3
: "memory",
"cc"
);
return CompareValue;
}