Reporting Issues
Bug 1516 - Implement shared compiler intrinsics library
Summary: Implement shared compiler intrinsics library
Status: CONFIRMED
Alias: None
Product: Tianocore Feature Requests
Classification: Unclassified
Component: Code (show other bugs)
Version: Current
Hardware: All Other
: Normal enhancement
Assignee: nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-02-06 05:06 UTC by Ryszard Knop
Modified: 2021-07-27 05:05 UTC (History)
2 users (show)

See Also:
EDK II Code First industry standard specifications: ---
Branch URL:
Release(s) the issue is observed:
The OS the target platform is running: ---
Package: MdePkg
Release(s) the issues must be fixed: EDK II Master
Tianocore documents:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ryszard Knop 2019-02-06 05:06:15 UTC
Compilers insert inline calls to various functions implementing more complicated math operations or memory manipulation. These functions are implemented in the standard library normally shipped with compilers, but as we use a free-standing environment, these are not available on some architectures, causing various build issues with code using such instructions.

An intrinsic library should be added to MdePkg with the most common intrinsics required. There is already an CompilerIntrinsicsLib in ArmPkg that implements all of these, but for ARM only.

IntelUndiPkg (in edk2-staging) currently uses:
- Multiplying: (U)INT64 * (U)INT64 (_allmul)
- Dividing INT64 / INT64 (_alldiv)
- Dividing UINT64 / UINT64 (_aulldiv)
- Left Shift INT64 (_allshl)
- Right Shift UINT64 (_aullshr)
- Memory ops (memcpy, memset)

CryptoPkg (in edk2) currently uses:
- Left Shift INT64 (_allshl)
- Right Shift UINT64 (_aullshr)
- Memory ops (memcpy, memset, memcmp, strcmp)
Comment 1 Ryszard Knop 2019-02-06 05:12:42 UTC
There are also some intrinsics implemented in StdLib/LibC/CRT and wrappers around EDK2 functions in EmbeddedPkg/Include/libfdt_env.h.
Comment 2 Laszlo Ersek 2019-02-06 12:14:21 UTC
Hi Ryszard,

BaseLib already provides APIs for these operations explicitly, and the
edk2 coding style requires programmers to explicitly use them (or to
avoid the intrinsics by staying away from otherwise valid C constructs).

Please refer to "MdePkg/Include/Library/BaseLib.h":

- multiplication, division, and bit shifting expression, up to and
  including the UINTN / INTN types, can be written with the normal C
  operators. For INT64/UINT64, the functions are:
  - MultU64x32
  - MultU64x64
  - MultS64x64
  - DivU64x32
  - ModU64x32
  - DivU64x32Remainder
  - DivU64x64Remainder
  - DivS64x64Remainder
  - LShiftU64
  - RShiftU64
  - ARShiftU64
  - (rotation services and other bit manipulation skipped for now)

- memcpy and memset intrinsics are supposed to be avoided by:
  - avoiding initialization of aggregate objects (structures, arrays)
    that have automatic storage duration ("locals")
  - avoiding structure assignment
  - explicitly calling CopyMem), SetMem*, ZeroMem from BaseMemoryLib
    instead [MdePkg/Include/Library/BaseMemoryLib.h].

- for comparison,
  - BaseMemoryLib offers CompareMem, CompareGuid.
  - BaseLib offers StrCmp, StrnCmp, AsciiStrCmp, AsciiStriCmp,
    AsciiStrnCmp.

The only reason for having implemented some compiler intrinsics for ARM
is that the corresponding compilers simply couldn't be convinced to stop
generating calls to said intrinsics. It's not a feature, but a
workaround. Please see the following commits:

- e0c2cc6f8975 ("Fix Xcode, clang, and ARM build and link issues.",
                2011-04-07)
- 8e01b449de91 ("Patch from open source community for CryptoPkg to allow
                it to build for ARM using the RVCT toolchain.",
                2011-04-19)
- 11c20f4e06d2 ("Arm Packages: Fixed coding style/Line endings to follow
                EDK2 coding convention", 2011-09-22)
- 262b7a311cbd ("FmpDevicePkg: Add DSC file to build all package
                components", 2018-08-02)
- dd178f0f9391 ("IntelFrameworkModulePkg: fix build for AARCH64/ARM",
                2019-02-04)
- 2f0a1e6542fd ("SecurityPkg: fix package build on ARM", 2019-02-04)
- 3b6c73f13eac ("SignedCapsulePkg: enable package build for
                AARCH64/ARM", 2019-02-04)

They all add a NULL class resolution with the comment

> # It is not possible to prevent the ARM compiler for generic intrinsic functions.
> # This library provides the instrinsic functions generate by a given compiler.
> # And NULL mean link this library into all ARM images.

If IntelUndiPkg was genuinely written for edk2, in my opinion it should
be updated to conform to the edk2 coding style.

CryptoPkg has an excuse for "CryptoPkg/Library/IntrinsicLib" --
CryptoPkg wraps OpenSSL, which is an external project.

I think we shouldn't add any intrinsics unless we are absolutely forced
to. I do agree however that, for those intrinsics that we cannot at all
avoid reimplementing, we should at least collect them in a common
library.

(In theory, I can also imagine reimplementing all possible intrinsics
*if* the edk2 coding style spec / requirements are updated in parallel,
permitting all new code to universally rely on the intrinsics, rather
than the BaseLib / BaseMemoryLib functions.)

Thanks!
Comment 3 Ryszard Knop 2019-02-07 04:59:26 UTC
Hi Laszlo,

I've explained this on the mailing list, but basically IntelUndiPkg shares a large part of its code with other *nix drivers. They liberally use memcpy/memset, assign structures, use 64-bit math ops and so on - we can't convince them to wrap such operations, as everything they do is valid C (as you've noted, too).

Right now a workaround is to simply ship a small library with needed intrinsics alongside IntelUndiPkg, but there's one here, one in CryptoPkg, also one needed for ARM builds...

Simply having standard intrinsics + having coding standards allow these, without having to work around the lack of those would be much nicer, I think. Especially that this would simplify porting and sharing code with non-EDK2 components as well whenever needed.

See related messages on edk2-devel:
- https://lists.01.org/pipermail/edk2-devel/2019-January/036027.html
- https://lists.01.org/pipermail/edk2-devel/2019-January/036032.html
- https://lists.01.org/pipermail/edk2-devel/2019-February/036366.html
Comment 4 Laszlo Ersek 2019-02-07 07:13:53 UTC
Thanks for the explanation.

I think we should follow one of two paths.

One path is that edk2 embeds an independent copy of the external code. In this case it should be adapted to the edk2 coding style. Repeated updates (code drops) from the external code base would be difficult, which is why such updates (code drops) should not be done in the first place. Maintenance would be manual (individual ports of the external changes).

The other path is that edk2 should consume the external project as-is, via another git submodule. In this case stuff that gets "plugged" into the external code is acceptable IMO, although in some cases the external project would have to be willing to take patches -- not necessarily the kind of wrapping that we discussed above, but some kind of refactoring should be possible, so that edk2 can consume that project more easily. This has been successful with OpenSSL; over time we've dropped basically all of the edk2-specific customization (patches) for the upstream code itself.

What I think is sub-optimal is a half-solution in the middle. Repeated upstream code drops (deep copies in the edk2 tree) combined with an ever changing plugin/wrapper library.

I'm acutely aware that the drivers incorporated / wrapped by IntelUndiPkg are not the first such example in edk2. We have the LZMA SDK, Brotli, libfdt, Oniguruma, the list goes on and on. We should consume all those external projects via git submodules.

Anyway, it's not my intent to block this work.
Comment 5 Ryszard Knop 2019-02-07 08:51:52 UTC
I understand the intent is to land with a clean solution, but one might not be easily possible at least in case of IntelUndiPkg. Let's say an external, not publicly accessible project accepts some patches, but still requires preprocessing/very ugly patching to build under EDK2. A submodule might be fine, but the result is that code drops would occur to a separate patched repository in parallel with regular commits to EDK2, which basically just adds a bit more complexity package maintainers have to work with, while not really providing any benefits.

But then this is the result of some of our constraints, not what inclusion of most open libraries would have to deal with. Having submodules for external libs where possible is absolutely desired, and having a sensible intrinsics library would make including them easier, too. Although I'm not the best person to decide or have opinions on this, given the amount of my contributions in EDK2, hovering around 0 :) Feel free to pick any solution you think is the best for this.
Comment 6 Yonghong Zhu 2019-02-21 19:34:37 UTC
discuss this one in the design meeting.