static const structures declared as "external global %struct.S" instead of "external constant %struct.S"

Hi,

I have a problem relating to the way clang handles static const structure declarations in c++ classes.

If only the declaration is visible:
class K {static const struct S k; …}
the constness is not emitted in the declaration:
@_ZN1K1kE = external global %struct.S
should this not be:
@_ZN1K1kE = external constant %struct.S

If the definition is visible too:
class J {static const struct S j;…}
const struct S J::j = {1,2};
we get a global ‘const’ definition, viz:
@_ZN1J1jE = constant %struct.S { i32 1, i32 2 }, align 4

It should be noted that the handling of arrays always uses const - see below.

Am I correct in believing this is a bug?
Can anyone direct me to where the problem may reside?

This is a problem on the XCore target as we handle constant and non-constant data in separate areas and hence will emit different assembler instructions.

Robert

struct S{int s1; int s2;};

class I {
static const int i[2];
static int ii();
};
int I::ii(){return i[0];}
// I::i defined else where - OK

class J {
static const struct S j;
static struct S jj();
};
struct S J::jj(){return j;}
const struct S J::j = {1,2};

class K {
static const struct S k;
static struct S kk();
};
struct S K::kk(){return k;}
// K::k defined else where - const is lost

%struct.S = type { i32, i32 }
@_ZN1I1iE = external constant [2 x i32]
@_ZN1J1jE = constant %struct.S { i32 1, i32 2 }, align 4
@_ZN1K1kE = external global %struct.S

define i32 @_ZN1I2iiEv() #0 align 2 {
entry:
%0 = load i32* getelementptr inbounds ([2 x i32]* @_ZN1I1iE, i32 0, i32 0), align 4
ret i32 %0
}

define void @_ZN1J2jjEv(%struct.S* noalias sret %agg.result) #0 align 2 {
entry:
%0 = bitcast %struct.S* %agg.result to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* bitcast (%struct.S* @_ZN1J1jE to i8*), i32 8, i32 4, i1 false)
ret void
}

define void @_ZN1K2kkEv(%struct.S* noalias sret %agg.result) #0 align 2 {
entry:
%0 = bitcast %struct.S* %agg.result to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* bitcast (%struct.S* @_ZN1K1kE to i8*), i32 8, i32 4, i1 false)
ret void
}

Hi,

I have narrowed the issue down to CodeGenModule::GetOrCreateLLVMGlobal()
This I assume is due to the uncertainty of whether a ctor/dtor will need to run on the ‘const’ data.

During CodeGenModule::EmitGlobalVarDefinition() the code explicitly checks the ctor/dtor situation:
// If it is safe to mark the global ‘constant’, do so now.
GV->setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor &&
isTypeConstant(D->getType(), true));

However this is only done for definitions and not declarations.
Should declarations be handled in a similar way?

Dumping the state of the ‘VarDecl *D’ for the example in the previous emails gives:

GetOrCreateLLVMGlobal
VarDecl 0x333c230 <test.cpp:5:3, col:23> i ‘const int [2]’ static
const int identifier[2]
isTypeConstant true

GetOrCreateLLVMGlobal
VarDecl 0x337eed0 <test.cpp:13:3, col:25> j ‘const struct S’:‘const struct S’ static
const struct S identifier
isTypeConstant false

EmitGlobalVarDefinition
GetOrCreateLLVMGlobal
VarDecl 0x337f7f0 parent 0x337ece0 prev 0x337eed0 <test.cpp:17:1, col:27> j ‘const struct S’:‘const struct S’
`-InitListExpr 0x337f920 <col:23, col:27> ‘const struct S’:‘const struct S’

-IntegerLiteral 0x337f890 col:24 ‘int’ 1
`-IntegerLiteral 0x337f8b0 col:26 ‘int’ 2
const struct S identifier
NeedsGlobalCtor false
NeedsGlobalDtor false
isTypeConstant true

GetOrCreateLLVMGlobal
VarDecl 0x337fb80 <test.cpp:21:3, col:25> k ‘const struct S’:‘const struct S’ static
const struct S identifier
isTypeConstant false

Hi,

I have a problem relating to the way clang handles static const structure
declarations in c++ classes.

If only the declaration is visible:
    class K {static const struct S k; ...}
the constness is not emitted in the declaration:
    @_ZN1K1kE = external global %struct.S
should this not be:
    @_ZN1K1kE = external constant %struct.S

If the definition is visible too:
    class J {static const struct S j;...}
    const struct S J::j = {1,2};
we get a global 'const' definition, viz:
    @_ZN1J1jE = constant %struct.S { i32 1, i32 2 }, align 4

It should be noted that the handling of arrays always uses const - see
below.

Am I correct in believing this is a bug?
Can anyone direct me to where the problem may reside?

This is a problem on the XCore target as we handle constant and
non-constant data in separate areas and hence will emit different assembler
instructions.

The 'k' object may or may not be able to be marked 'constant' (depending on
its initializer). Some LLVM passes will promote it from 'global' to
'constant', too. If the instructions you emit for 'global' reads don't work
on 'constant' objects and vice versa, that's a bug in your target. Here's a
quote from the LangRef:

"LLVM explicitly allows *declarations* of global variables to be marked
constant, even if the final definition of the global is not. This
capability can be used to enable slightly better optimization of the
program, but requires the language definition to guarantee that
optimizations based on the ‘constantness’ are valid for the translation
units that do not include the definition."

Robert

The type of the global doesn’t have to be complete, so clang can’t really know if it will be constant. This is valid:

struct G;
extern const G g;
void fun(const G *g);
int main() {
fun(&g);
}

However, it seems reasonable to make the declaration constant as an optimization if we happen to have the complete type.

Reid

Currently the XCore backend choses how to access the global based on whether it is declared as constant in the LLVM IR. If it is constant it is placed in the .cp.rodata section the accessed via the cp register, otherwise it is placed in the .dp.data section and it is accessed via the dp register.

In retrospect this is the wrong approach - it breaks when the constness of the global in LLVM IR differs across translation units and there is no way to avoid this with C++. I think the right fix is to move the decision of where to place the global (which is an ABI issue) to the frontend where the decision can be based on the high level type. In particular clang should explicitly specify the .cp.rodata section on globals that need to go in the cp according the XCore ABI. In the absence of an explicit section the XCore backend should place all globals in the .dp.data or .dp.rodata sections.

Hi Richard, Reid,

Thank you for the replies - much appreciated.
I saw the language reference below, but was unsure if it worked in both directions viz: declarations non const & definitions const.
However, it makes little difference if it is one way or both - the point is I need to fix it (as per Richards Osborne’s comments).

thank you
Robert