Re: [edk2] Need for FixedFeaturePcdGet() ?

Subject: Re: [edk2] Need for FixedFeaturePcdGet() ?

From: Andrew Fish <>


Date: 2014-01-23 19:02:37

On Jan 23, 2014, at 10:27 AM, Olivier Martin <> wrote:

I have only tried with GCC.
In the use case I used in my previous email, _gPcd_FixedAtBuild_PcdRelocateVectorTable is initialized by another C file.
I am not a compiler expert, but I guess your compiler would need at least two passes to optimise this specific code once he knows the value of const BOOLEAN _gPcd_FixedAtBuild_PcdRelocateVectorTable from another compilation unit.


The 2nd pass is link time code generation. Visual Studio does it via linker flags, and you can turn it on in clang with -flto. For clang this tells the compiler to emit LLVM bitcode objects (kind of a machine independent assembly language that gets assembled). The linker combines all the bitcode together in the link stage and only at that point is native code generated. Thus whole program optimization is possible so the dead striping works. 

As far as I know GCC does not support this. 


Andrew Fish

We have recently done some investigation to add two build passes in BaseTools to take advantage of the ARM linker feedback (see
But we realized that would not be easy to maintain in build_rules.txt.
From: Tim Lewis [] 
Sent: 23 January 2014 18:09
Subject: Re: [edk2] Need for FixedFeaturePcdGet() ?
I think this came about because VS always removes the dead code in the release build.
However, I agree about the doesnt appear in the .DSC problem. It has forced us on a number of occasions to add dummy .DSC entries that have the exact same default value as the .DEC.
From: Olivier Martin [] 
Sent: Thursday, January 23, 2014 10:00 AM
Subject: [edk2] Need for FixedFeaturePcdGet() ?
Feature PCDs are mainly (only?) used to disable features (ie: removing code) at build type.
We found that it actually never removes code. Here is an example (ArmPkg/Drivers/CpuDxe/ArmV6/Exception.c):
  ArmDisableFiq ();
  if (FeaturePcdGet(PcdRelocateVectorTable) == TRUE) {
  } else {
    // The Vector table must be 32-byte aligned
    ASSERT(((UINT32)ExceptionHandlersStart & ((1 << 5)-1)) == 0);
    // We do not copy the Exception Table at PcdGet32(PcdCpuVectorBaseAddress). We just set Vector Base Address to point into CpuDxe code.
    ArmWriteVBar ((UINT32)ExceptionHandlersStart);
If we build it with the upstream UEFI. The C code becomes after pre-preprocessing:
  ArmDisableFiq ();
  if (_gPcd_FixedAtBuild_PcdRelocateVectorTable == ((BOOLEAN)(1==1))) {
And the dissassembly:
     bl   ArmDisableFiq
     .loc 1 147 0
     ldr  r3, .L44+4  ; Get the address of _gPcd_FixedAtBuild_PcdRelocateVectorTable
     ldrb r3, [r3]    ; Load its value
     cmp  r3, #1      ; if (_gPcd_FixedAtBuild_PcdRelocateVectorTable == ((BOOLEAN)(1==1))) {
     bne  .L19
     .loc 1 151 0
     ldr  r3, .L44+8
Now, let's have a look at the AutoGen.h for this PCD:
#define _PCD_TOKEN_PcdRelocateVectorTable  104U
#define _PCD_VALUE_PcdRelocateVectorTable  ((BOOLEAN)0U)
extern const  BOOLEAN  _gPcd_FixedAtBuild_PcdRelocateVectorTable;
#define _PCD_GET_MODE_BOOL_PcdRelocateVectorTable  _gPcd_FixedAtBuild_PcdRelocateVectorTable
//#define _PCD_SET_MODE_BOOL_PcdRelocateVectorTable  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD
And the definition of FeaturePcdGet() in PcdLib.h:
#define FeaturePcdGet(TokenName)            _PCD_GET_MODE_BOOL_##TokenName
I hacked PcdLib to used its value instead of accessing the global variable:
#define FeaturePcdGet(TokenName)            _PCD_VALUE_##TokenName
But this change was not enough because PCDs that were not set in DSC did not have their _PCD_VALUE_ value.
Example of PcdVerifyNodeInList:
#define _PCD_TOKEN_PcdVerifyNodeInList  5U
extern const BOOLEAN _gPcd_FixedAtBuild_PcdVerifyNodeInList;
#define _PCD_GET_MODE_BOOL_PcdVerifyNodeInList  _gPcd_FixedAtBuild_PcdVerifyNodeInList
//#define _PCD_SET_MODE_BOOL_PcdVerifyNodeInList  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD
So, I hacked BaseTools to always add the value of PCDs:
--- a/BaseTools/Source/Python/AutoGen/
+++ b/BaseTools/Source/Python/AutoGen/
@@ -1077,6 +1077,8 @@ def CreateLibraryPcdCode(Info, AutoGenC, AutoGenH, Pcd):
         if PcdItemType == TAB_PCDS_FIXED_AT_BUILD and key in Info.ConstPcd:
             AutoGenH.Append('#define _PCD_VALUE_%s %s\n' %(TokenCName, Pcd.DefaultValue))
+        else:
+            AutoGenH.Append('#define _PCD_VALUE_%s %s\n' %(TokenCName, Pcd.DefaultValue)
After rebuilding my platform (ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A9x4.dsc) in RELEASE build, here is the result:
     bl   ArmDisableFiq
     .loc 1 215 0
     ldr  r0, .L25+4
     bl   ArmWriteVBar
We can now see the dead code has been removed.
In term of size:
FVMAIN_SEC [5%Full] 524288 total, 27232 used, 497056 free
FVMAIN_COMPACT [18%Full] 2621440 total, 481760 used, 2139680 free
FVMAIN [99%Full] 1161088 total, 1161056 used, 32 free
FVMAIN_SEC [5%Full] 524288 total, 27232 used, 497056 free
FVMAIN_COMPACT [18%Full] 2621440 total, 478632 used, 2142808 free
FVMAIN [99%Full] 1154432 total, 1154400 used, 32 free
So, it saved 6656 bytes in the non-compressed FV.
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today.
edk2-devel mailing list