This is an archive of the discontinued LLVM Phabricator instance.

[X86] Save/restore base pointer register when it is clobbered
AbandonedPublic

Authored by LuoYuanke on Feb 22 2023, 12:52 AM.

Details

Summary

The base pointer register is reserved by compiler when there is
dynamic size alloca and stack realign in a function. However the
base pointer register is not defined in X86 ABI, so user can use
this register in inline assembly. The inline assembly would
clobber base pointer register without being awared by user. This
patch to save and restore the base pointer register when compiler
detect it is clobbered. Below is the example code for such case.

extern int bar(void *p);
long long foo(size_t size, char c, int id) {
  __attribute__((__aligned__(64))) int a;
  char *p = (char *)alloca(size);
  asm volatile ("nop"::"S"(405):);
  asm volatile ("movl %0, %1"::"r"(id), "m"(a):);
  p[2] = 8;
  memset(p, c, size);
  return bar(p);
}

Diff Detail

Event Timeline

LuoYuanke created this revision.Feb 22 2023, 12:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 22 2023, 12:52 AM
LuoYuanke requested review of this revision.Feb 22 2023, 12:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 22 2023, 12:52 AM

Incorrect title? You’re saving and restoring not disabling right?

LuoYuanke retitled this revision from [X86] Disable base pointer register when it is clobbered to [X86] Save/restore base pointer register when it is clobbered.Feb 22 2023, 12:56 AM

Incorrect title? You’re saving and restoring not disabling right?

Thanks, Craig. Changed the title.

craig.topper added inline comments.Feb 22 2023, 12:57 AM
llvm/test/CodeGen/X86/x86-64-baseptr.ll
126

If ebx needs to be restored how can you use it in the address?

LuoYuanke added inline comments.Feb 22 2023, 1:01 AM
llvm/test/CodeGen/X86/x86-64-baseptr.ll
126

Opps, l'll refine the patch. Thanks, Craig.

pengfei accepted this revision.Feb 22 2023, 1:29 AM

Looks great!

This revision is now accepted and ready to land.Feb 22 2023, 1:29 AM
LuoYuanke updated this revision to Diff 499454.Feb 22 2023, 5:00 AM

Address Craig's comments.

LuoYuanke added inline comments.Feb 22 2023, 5:03 AM
llvm/test/CodeGen/X86/i386-baseptr.ll
44

I'll check if the disp is set right.

LuoYuanke added inline comments.Feb 23 2023, 1:14 AM
llvm/test/CodeGen/X86/i386-baseptr.ll
44

It seems there is a bug in https://reviews.llvm.org/D6388, I create https://reviews.llvm.org/D144625 to fix this bug.

LuoYuanke updated this revision to Diff 500425.Feb 25 2023, 6:28 AM

Rebase and update test case.

What happens if an inline asm both has a memory reference, and clobbers the base pointer? How do we refer to memory?

What happens if an inline asm both has a memory reference, and clobbers the base pointer? How do we refer to memory?

That's a good question. Maybe exchange ebp and esi. Base pointer (esi) is used to reference the stack slot before realignment and frame pointer (ebp) is used to reference stack slot after realignment. Since ebp is reserved in ABI, user can not clobber ebp in inline assembly. What do you think?

What happens if an inline asm both has a memory reference, and clobbers the base pointer? How do we refer to memory?

That's a good question. Maybe exchange ebp and esi. Base pointer (esi) is used to reference the stack slot before realignment and frame pointer (ebp) is used to reference stack slot after realignment. Since ebp is reserved in ABI, user can not clobber ebp in inline assembly. What do you think?

Possible alternatives:

  • Use ebp... but this messes with unwinding, and doesn't actually solve the issue in general (if the inline asm references function arguments, those references are ebp-relative).
  • Use some other register not used by the inline asm as a temporary base pointer.
  • We use a different prologue that avoids the need for a base pointer. See discussion at https://github.com/llvm/llvm-project/issues/17204

Possible alternatives:

  • Use ebp... but this messes with unwinding, and doesn't actually solve the issue in general (if the inline asm references function arguments, those references are ebp-relative).

Why it would mess unwinding? The cfi instruction can indicate %esi to compute CFA. The assembly code may look like below. Yes, there is still issue if inline asm references function arguments after modifying %esi.

        .cfi_startproc
# %bb.0:                                # %entry
        pushl   %esi
        .cfi_def_cfa_offset 8
        .cfi_offset %esi, -8
        movl    %esp, %esi       // esi is the frame pointer
        .cfi_def_cfa_register %esi
        pushl   %ebx
        pushl   %ebp
        andl    $-128, %esp
        subl    $128, %esp
        movl    %esp, %ebp        // ebp is the base pointer
        .cfi_offset %ebp, -16
        .cfi_offset %ebx, -12
        ...
  • Use some other register not used by the inline asm as a temporary base pointer.

That register (other register) may also be clobbered in another inline asm. We need analyze all the inline asm of a function to choose a base pointer register.

It seems gcc generate special cfi information (cfi_escape) after realign stack. Don't know what ech cif_escape means. Could you write some psuedo code for the prologue?

[llvm]$ cat t.cpp
#include <alloca.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>

extern int bar(void *p);
long long foo(size_t size, char c, int id) {
  __attribute__((__aligned__(128))) int a;
  char *p = (char *)alloca(size);
  asm volatile ("nop"::"c"(405):);
  asm volatile ("movl %0, %1"::"r"(id), "m"(a), "S"(405):);
  p[2] = 8;
  memset(p, c, size);
  return bar(p);
}

[llvm]$ g++ t.cpp -m32 -S -O2 -o -
        .file   "t.cpp"
        .text
        .p2align 4
        .globl  _Z3foojci
        .type   _Z3foojci, @function
_Z3foojci:
.LFB33:
        .cfi_startproc
        leal    4(%esp), %ecx
        .cfi_def_cfa 1, 0
        andl    $-128, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        .cfi_escape 0x10,0x5,0x2,0x75,0
        pushl   %edi
        pushl   %esi
        pushl   %ebx
        pushl   %ecx
        .cfi_escape 0xf,0x3,0x75,0x70,0x6
        .cfi_escape 0x10,0x7,0x2,0x75,0x7c
        .cfi_escape 0x10,0x6,0x2,0x75,0x78
        .cfi_escape 0x10,0x3,0x2,0x75,0x74

Why it would mess unwinding?

I meant fp-based unwinding for profiling etc., not DWARF unwind.

That register (other register) may also be clobbered in another inline asm

We don't have to use the same register for each inline asm.

Don't know what ech cif_escape means

llvm-readobj --unwind can decode the DWARF for you.

The important thing is that it allocates a variable amount of space between the arguments and the frame pointer, and saves the incoming value of esp on the stack.

Why it would mess unwinding?

I meant fp-based unwinding for profiling etc., not DWARF unwind.

That register (other register) may also be clobbered in another inline asm

We don't have to use the same register for each inline asm.

The base pointer register is reserved by compiler, we can't reserve too much register?

Don't know what ech cif_escape means

llvm-readobj --unwind can decode the DWARF for you.

The important thing is that it allocates a variable amount of space between the arguments and the frame pointer, and saves the incoming value of esp on the stack.

It seems this option is the best one. What do you think?

That register (other register) may also be clobbered in another inline asm

We don't have to use the same register for each inline asm.

The base pointer register is reserved by compiler, we can't reserve too much register?

You wouldn't reserve a register, just add some instructions around the inline asm to save/restore the base pointer. It's sort of messy, but probably workable.

Don't know what ech cif_escape means

llvm-readobj --unwind can decode the DWARF for you.

The important thing is that it allocates a variable amount of space between the arguments and the frame pointer, and saves the incoming value of esp on the stack.

It seems this option is the best one. What do you think?

This is the most straightforward/maintainable, probably.

LuoYuanke added a comment.EditedMar 8 2023, 10:03 PM

This is the most straightforward/maintainable, probably.

I create https://reviews.llvm.org/D145650 for it, but it seems that is more complex and bug-prone. Since this patch fix part of the issue, can we land this patch first?

LuoYuanke abandoned this revision.Mar 20 2023, 5:55 PM