How to remove memcpy

Hi,

I am hoping that someone can help me figure out how to prevent the insertion of “memcpy” from the assembly source.

My target is an instruction set simulator that doesn’t support this.

Thank you for your valuable time.

Wolf

Here are my compile commands:
$ clang -emit-llvm -fno-builtin -o3 --target=mips -S matrix_float.c -o vl_matrix_float.ll

$ llc vl_matrix_float.ll

IR File Snip:

%0 = bitcast [10 x [10 x float]]* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* bitcast ([10 x [10 x float]]* @main.a to i8*), i32 400, i32 4, i1 false)
%1 = bitcast [10 x [10 x float]]* %b to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %1, i8* bitcast ([10 x [10 x float]]* @main.b to i8*), i32 400, i32 4, i1 false)
store i32 0, i32* %sum, align 4

Assembly File Snip:

BB#0: # %entry

lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -1664
sw $ra, 1660($sp) # 4-byte Folded Spill
sw $fp, 1656($sp) # 4-byte Folded Spill
sw $17, 1652($sp) # 4-byte Folded Spill
sw $16, 1648($sp) # 4-byte Folded Spill
move $fp, $sp
addu $17, $2, $25
lw $1, %got($main.a)($17)
addiu $5, $1, %lo($main.a)
lw $25, %call16(memcpy)($17)
addiu $16, $fp, 1248
move $4, $16
addiu $6, $zero, 400
jalr $25
move $gp, $17
lw $1, %got($main.b)($17)
addiu $5, $1, %lo($main.b)
lw $25, %call16(memcpy)($17)
addiu $17, $fp, 848
move $4, $17
jalr $25
addiu $6, $zero, 400
sw $zero, 820($fp)
sw $zero, 844($fp)
addiu $2, $fp, 420
b $BB0_2
addiu $3, $fp, 20
$BB0_1:

Technically -fno-bultin prevents the compiler from understand the memset in the original code. The right option to prevent the compiler from insert libc calls “out-of-the-blue” is -ffreestanding.

However I thought that right now clang does not differentiate these, so your result is somehow strange.
Can you include the matrix_float.c source code?

Unless your backend explicitly always inlines memcpy, this is expected.
In a lot of situation, creating a libcall for non-trivial sizes is far
preferable than an inlined loop.

Joerg

Huh? The -fno-builtin is not the problem. The compiler is *expected* to
call certain functions even for -ffreestanding. memcpy and memset are
two of those. It is certainly perfectly valid target lowering for
llvm.memcpy to be turned back into a libcall.

Joerg

Hi Mehdi and Joerg,

Thanks for your fast response! attached is the matrix_float.c file.

Also - I should point out that I am using MIPS ISS.

Thanks,

Wolf

matrix_float.c (2.64 KB)

Even with -ffreestanding LLVM generates memcpy/memset? Does this mean some passes do not honor this flag? If you really wanted to prevent libcalls, you could technically translate those memcpy/memset to loops in lowering.
Kevin

Are you intentionally not using static (& const) arrays? The compiler has
to use a copy to initialize a & b, given the size, it will use a memcpy
from a read-only section for that.

Joerg

Hi,

I am hoping that someone can help me figure out how to prevent the insertion of “memcpy” from the assembly source.

My target is an instruction set simulator that doesn’t support this.

Thank you for your valuable time.

Wolf

Here are my compile commands:
$ clang -emit-llvm -fno-builtin -o3 --target=mips -S matrix_float.c -o vl_matrix_float.ll

Technically -fno-bultin prevents the compiler from understand the
memset in the original code. The right option to prevent the compiler
from insert libc calls “out-of-the-blue” is -ffreestanding.

Huh? The -fno-builtin is not the problem.

I’m not sure what you mean by “it is not the problem” or how it relate to what I wrote.

Many people use -fno-builtin thinking it’ll prevent the compiler from calling memset. I’m pointing this isn’t true (the command line he posted includes -fno-builtin).

The compiler is expected to
call certain functions even for -ffreestanding. memcpy and memset are
two of those. It is certainly perfectly valid target lowering for
llvm.memcpy to be turned back into a libcall.

Source?
AFAICT this is GCC behavior, but not the standard: https://gcc.gnu.org/onlinedocs/gcc/Standards.html#Standards

Yes, LLVM follows the same rules as GCC here:

  GCC requires the freestanding environment provide `memcpy', `memmove',
  `memset' and `memcmp'.

As I said, target lowering may decide to inline all the llvm.mem*
intrinsics, but it is typically wasteful to do so. Exceptions are e.g.
on x86 when optimising for size, since the function call overhead is
large than the "rep stosb" sequence would be for memset or "rep movsb"
or memcpy.

Joerg

No, there is no reason that can’t be static. How do you recommend that I define them?

THanks!

Also - I tried both -fno-builtin and -ffreestanding and code with memcpy was still produced.

Just make them static const, so that a & b are effectively read-only
global variables. That way, the function doesn't have to reinitialize
them all the time.

Joerg

That worked!

Thank you for your time!

>> Technically -fno-bultin prevents the compiler from understand the
>> memset in the original code. The right option to prevent the compiler
>> from insert libc calls “out-of-the-blue” is -ffreestanding.
>
> Huh? The -fno-builtin is not the problem.

I’m not sure what you mean by “it is not the problem” or how it relate to what I wrote.

Many people use -fno-builtin thinking it’ll prevent the compiler from
calling memset. I’m pointing this isn’t true (the command line he
posted includes -fno-builtin).

The original code contains no memset (nor memcpy). As such, -fno-builtin
does not change the interpretation of the program as far as lowering to
IR goes. The backend using memset internally is a completely unrelated
problem.

> The compiler is *expected* to
> call certain functions even for -ffreestanding. memcpy and memset are
> two of those. It is certainly perfectly valid target lowering for
> llvm.memcpy to be turned back into a libcall.

Source?
AFAICT this is GCC behavior, but not the standard:
Standards (Using the GNU Compiler Collection (GCC))

The standard has almost nothing to say about how freestanding really
works. It has a few requirements on what must be working in freestanding
environments, but the precise contract of what the compiler expects as
part of the ABI is outside the scope of the standard. Helper functions
for implementing division in software certainly fall into this scope as
well. The second-to-last paragraph are the lines about requirements GCC
puts on the environment, the same essentially applies to LLVM.

Joerg