18

I am building an EFI driver and therefore cannot use stdlib, so no memset. I am also running after ExitBootServices, so no edk2 CopyMem. This is my function:

void Set_Memory(VOID* Dest, UINTN Len, CHAR8 Val)
{
    for (int i = 0; i < Len; ++i)
    {
        ((UINT8*)Dest)[i] = Val;
    }
}

When compiling with optimizations, I get LNK2001 unresolved external symbol memset. Presumably the MSVC compiler is replacing my call to Set_Memory with a call to memset. I also cannot define my own memset, because I get the error C2169 'memset': intrinsic function, cannot be defined. How can I prevent this without losing other optimizations?

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • Welcome to Stack Overflow. Please read the [About](http://stackoverflow.com/tour) page soon and also visit the links describing [How to Ask a Question](http://stackoverflow.com/questions/how-to-ask) and [How to create a Minimal Complete Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). Providing the necessary details, including your MCRE, compiler warnings and associated errors, and sample data if any, will allow everyone here to help you with your question. That said, MSVC will not replace `Set_Memory()` with `memset()`, the issue isn't in what is posted. – David C. Rankin Mar 05 '23 at 06:21
  • 4
    @DavidC.Rankin It will replace the body of the function with a call to memset and that's common behavior for optimizing compilers: https://godbolt.org/z/5s1hxEr13 – user17732522 Mar 05 '23 at 06:58
  • @user17732522 - Oh, that's interesting. So based on the internal behavior (the resulting assembly?), MSVC says "that's `memset()`" and makes the substitution. So much for programmer control... Surely there is an option (while still keeping all other optimizations) that will disable the substitution behavior? fortnite gamer -- I stand corrected. in the last sentence to my comment. – David C. Rankin Mar 05 '23 at 07:15
  • @DavidC.Rankin I know that the flag is `-fno-builtin` for GCC/Clang, but I don't know what it is for MSVC. I agree that there must be one to support environments like OP's. – user17732522 Mar 05 '23 at 07:21
  • You could [turn off optimizations for the one routine for MSVC](https://learn.microsoft.com/en-us/cpp/preprocessor/optimize?view=msvc-170). [Example](https://godbolt.org/z/4YncoTM9q) – Avi Berger Mar 05 '23 at 07:31
  • 3
    It is [intrinsic pragma](https://learn.microsoft.com/en-us/cpp/preprocessor/intrinsic?view=msvc-170) and you need to remove the `/Oi` optimization. (enabled by default in VS) `memset()` is a specific intrinsic used for replacement. See also [/Oi (Generate Intrinsic Functions)](https://learn.microsoft.com/en-us/cpp/build/reference/oi-generate-intrinsic-functions?view=msvc-170) @user17732522 -- found it `:)` The replacement means the `cl` determined `memset()` provided better performance and thus made the replacement (even though that doesn't help here) – David C. Rankin Mar 05 '23 at 07:36
  • 2
    This was a good question -- I learned something from the question itself, – David C. Rankin Mar 05 '23 at 07:40
  • @DavidC.Rankin I don't understand from the docs how this would help here. I tried playing around in Godbolt both with #pragma intrinsic and /Oi- and by trials not found either or both to produce the desired behavior. And as I understand it, /Oi would be a positive thing, as it enables inline expansion in place of a function call. – Avi Berger Mar 05 '23 at 08:04
  • @AviBerger from my read the problem was that `/Oi` is set by default in VS (and likely other IDEs) The pages referenced discuss how to remove the `/Oi` optimization to prevent the intrinsic replacement (though admittedly, the `/Oi[-]` page is completely silent on the use of the `[-]` suffix and you are left to presume it would disable the feature -- but that isn't stated.) – David C. Rankin Mar 05 '23 at 08:33
  • The compiler did not replace your call, its optimizer replaced your for-loop. It can see that it does the exact equivalent of memset(), so favored it. memcpy() is another one it relies on, happens when you copy a struct for example. The stdlib implementation of them are just fine in any runtime environment. – Hans Passant Mar 05 '23 at 08:39
  • fortnite gamer, Perhaps the confused compiler would be happier if _somewhere_ in this file a `memset()` was explicitly used? – chux - Reinstate Monica Mar 05 '23 at 08:47
  • 1
    Have you tried to define a memset function for the compiler with "#pragma function(memset)"? This should make the compiler use the function instead of his own intrinsic. – MiSimon Mar 06 '23 at 10:25

1 Answers1

2

Use the #pragma optimize("", off) to turn off optimizations for certain functions.

For example:

#pragma optimize("", off)
void Set_Memory(VOID* Dest, UINTN Len, CHAR8 Val)
{
    for (int i = 0; i < Len; ++i)
    {
        ((UINT8*)Dest)[i] = Val;
    }
}
#pragma optimize("", on)

optimize pragma is documented here

user20716902
  • 861
  • 1
  • 14
  • 1
    That doesn't seem a desirable solution though. I would still want to have the function produce performant code (e.g. using vectorization, etc., when possible), especially for such a function. It just shouldn't defer the optimization to a `memset` implementation. – user17732522 Mar 05 '23 at 17:13