How can I get struct's unaligned member with pointer?

I want to use pointer to get unalign data in struct.
my c code src and ll file.:

//=============input.c=================
#pragma pack(push)
#pragma pack(1)
struct S0 {
  short  f4;
  int  f5;
};
#pragma pack(pop)
struct S0 g_1 = {1,2};
/* ---------------------------------------- */
int main (void){
   int *x = &(g_1.f5);
   int y = *x;
   struct S0 l_1 = {3,4};
   int *z = &(l_1.f5);
   int w = *z;
   return 0;
}
//=======================input.ll=======
; ModuleID = 'D:\git_soft\compiler10\dsp_test_01\testcase_8slots\input.c'
source_filename = "D:\\git_soft\\compiler10\\dsp_test_01\\testcase_8slots\\input.c"
target datalayout = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
target triple = "dsp"

%struct.S0 = type <{ i16, i32 }>

@g_1 = dso_local global %struct.S0 <{ i16 1, i32 2 }>, align 1, !dbg !0
@__const.main.l_1 = private unnamed_addr constant %struct.S0 <{ i16 3, i32 4 }>, align 1

; Function Attrs: noinline nounwind optnone
define dso_local i32 @main() #0 !dbg !17 {
entry:
  %retval = alloca i32, align 4
  %x = alloca i32*, align 4
  %y = alloca i32, align 4
  %l_1 = alloca %struct.S0, align 1
  %z = alloca i32*, align 4
  %w = alloca i32, align 4
  store i32 0, i32* %retval, align 4
  call void @llvm.dbg.declare(metadata i32** %x, metadata !20, metadata !DIExpression()), !dbg !22
  store i32* getelementptr inbounds (%struct.S0, %struct.S0* @g_1, i32 0, i32 1), i32** %x, align 4, !dbg !22
  call void @llvm.dbg.declare(metadata i32* %y, metadata !23, metadata !DIExpression()), !dbg !24
  %0 = load i32*, i32** %x, align 4, !dbg !25
  %1 = load i32, i32* %0, align 4, !dbg !26
  store i32 %1, i32* %y, align 4, !dbg !24
  call void @llvm.dbg.declare(metadata %struct.S0* %l_1, metadata !27, metadata !DIExpression()), !dbg !28
  %2 = bitcast %struct.S0* %l_1 to i8*, !dbg !28
  call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %2, i8* align 1 bitcast (%struct.S0* @__const.main.l_1 to i8*), i32 6, i1 false), !dbg !28
  call void @llvm.dbg.declare(metadata i32** %z, metadata !29, metadata !DIExpression()), !dbg !30
  %f5 = getelementptr inbounds %struct.S0, %struct.S0* %l_1, i32 0, i32 1, !dbg !31
  store i32* %f5, i32** %z, align 4, !dbg !30
  call void @llvm.dbg.declare(metadata i32* %w, metadata !32, metadata !DIExpression()), !dbg !33
  %3 = load i32*, i32** %z, align 4, !dbg !34
  %4 = load i32, i32* %3, align 4, !dbg !35
  store i32 %4, i32* %w, align 4, !dbg !33
  ret i32 0, !dbg !36
}

; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1

; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #2

attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="dsp8s1280bdvic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable willreturn }
attributes #2 = { argmemonly nounwind willreturn }

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!13, !14, !15}
!llvm.ident = !{!16}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "g_1", scope: !2, file: !6, line: 8, type: !7, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0 (http://10.18.30.194:8888/compiler/compiler10.git b7b187637b0b4ac83d313da9b65310b2eea33a64)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None)
!3 = !DIFile(filename: "D:\\git_soft\\compiler10\\dsp_test_01\\testcase_8slots\\input.c", directory: "D:\\git_soft\\compiler10\\dsp_test_01\\scripts")
!4 = !{}
!5 = !{!0}
!6 = !DIFile(filename: "testcase_8slots\\input.c", directory: "D:\\git_soft\\compiler10\\dsp_test_01")
!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S0", file: !6, line: 3, size: 48, elements: !8)
!8 = !{!9, !11}
!9 = !DIDerivedType(tag: DW_TAG_member, name: "f4", scope: !7, file: !6, line: 4, baseType: !10, size: 16)
!10 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
!11 = !DIDerivedType(tag: DW_TAG_member, name: "f5", scope: !7, file: !6, line: 5, baseType: !12, size: 32, offset: 16)
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{i32 7, !"Dwarf Version", i32 2}
!14 = !{i32 2, !"Debug Info Version", i32 3}
!15 = !{i32 1, !"wchar_size", i32 4}
!16 = !{!"clang version 10.0.0 (http://10.18.30.194:8888/compiler/compiler10.git b7b187637b0b4ac83d313da9b65310b2eea33a64)"}
!17 = distinct !DISubprogram(name: "main", scope: !6, file: !6, line: 10, type: !18, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4)
!18 = !DISubroutineType(types: !19)
!19 = !{!12}
!20 = !DILocalVariable(name: "x", scope: !17, file: !6, line: 11, type: !21)
!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 32)
!22 = !DILocation(line: 11, column: 9, scope: !17)
!23 = !DILocalVariable(name: "y", scope: !17, file: !6, line: 12, type: !12)
!24 = !DILocation(line: 12, column: 8, scope: !17)
!25 = !DILocation(line: 12, column: 13, scope: !17)
!26 = !DILocation(line: 12, column: 12, scope: !17)
!27 = !DILocalVariable(name: "l_1", scope: !17, file: !6, line: 13, type: !7)
!28 = !DILocation(line: 13, column: 14, scope: !17)
!29 = !DILocalVariable(name: "z", scope: !17, file: !6, line: 14, type: !21)
!30 = !DILocation(line: 14, column: 9, scope: !17)
!31 = !DILocation(line: 14, column: 19, scope: !17)
!32 = !DILocalVariable(name: "w", scope: !17, file: !6, line: 15, type: !12)
!33 = !DILocation(line: 15, column: 8, scope: !17)
!34 = !DILocation(line: 15, column: 13, scope: !17)
!35 = !DILocation(line: 15, column: 12, scope: !17)
!36 = !DILocation(line: 16, column: 4, scope: !17)

I find global and local struct has diffent IR instruction.
my question is:
1.local unalign struct member how to pattern it?something like handle in func lowerload?

for (MVT VT : {MVT::i16, MVT::i32, MVT::f32, MVT::f64}) {
            setOperationAction(ISD::LOAD,  VT, Custom);
            setOperationAction(ISD::STORE, VT, Custom);
 }

2.global unaligned struct member? What should I do?

Do you mean the fact that the getelementptr is included in the store instruction when the struct is global, but a separate instruction on its own when local?

You shouldn’t have to do anything special in either case. The general CodeGen infrastructure knows how to handle both cases and will convert it to a simple pointer addition that your backend ought to be able to handle anyway.

Technically, the difference is that since all inputs for the global GEP are constants, it can be a ConstantExpr, which means it’s kind of a generic value that doesn’t live in any one basic block as an instruction. So when actually written in IR, ConstantExprs like that are inlined into the instruction that uses them.

On the other hand, a locally allocated struct needs a real GetElementPtrInst instruction to do the arithmetic, which has to be inserted into a basic block.

    unsigned int *x = &(g_28.f5);

This is invalid code, it is constructing a pointer to int, from a pointer-to-underaligned-int.

Sadly, we don’t have “Unaligned” represented in the type system, but still, code cannot do this. It must access the value directly through the packed struct so that the underalignment is known at access time.

Oh, well spotted. Though I think we do have underaligned in the C type system, it’s just really finnicky to get it to stick. The only way I know is typedef:

typedef int __attribute__((aligned(1))) unaligned_int;

Sadly, we don’t have “Unaligned” represented in the type system, but still, code cannot do this. It must access the value directly through the packed struct so that the underalignment is known at access time.

Actually, we do have the __unaligned MS extension, and it is part of clang’s type system. Unfortunately, MSVC is very permissive about conversion between unaligned pointers. Clang implements those rules, so it’s not very useful for alignment safety purposes. It is mostly just a way to generate loads and stores with align 1 on them: Compiler Explorer

Get it. Thanks.
But load global unaligned struct not work. My backend not convet global unaligned load32 to other instruction.

load local unaligned struct in legalize pass is work.

void SelectionDAGLegalize::LegalizeLoadOps(SDNode *Node) {
  LoadSDNode *LD = cast<LoadSDNode>(Node);
  SDValue Chain = LD->getChain();  // The chain.
  SDValue Ptr = LD->getBasePtr();  // The base pointer.
  SDValue Value;                   // The value returned by the load op.
  SDLoc dl(Node);

  ISD::LoadExtType ExtType = LD->getExtensionType();
  if (ExtType == ISD::NON_EXTLOAD) {
    LLVM_DEBUG(dbgs() << "Legalizing non-extending load operation\n");
    MVT VT = Node->getSimpleValueType(0);
    SDValue RVal = SDValue(Node, 0);
    SDValue RChain = SDValue(Node, 1);

    switch (TLI.getOperationAction(Node->getOpcode(), VT)) {
    default: llvm_unreachable("This action is not supported yet!");
    case TargetLowering::Legal: {
      EVT MemVT = LD->getMemoryVT();
      const DataLayout &DL = DAG.getDataLayout();
      // If this is an unaligned load and the target doesn't support it,
      // expand it.
      if (!TLI.allowsMemoryAccessForAlignment(*DAG.getContext(), DL, MemVT,
                                              *LD->getMemOperand())) {
        std::tie(RVal, RChain) = TLI.expandUnalignedLoad(LD, DAG);
      }
      break;
    }
......

TLI.allowsMemoryAccessForAlignment return false in local case, and it work.
but this func always return true in global case, so legalize pass can’t convert it.

CodeGen infrastructure handle global case in which pass?
Or my backend’s some configure is wrong, so It can not work?
My question is:
How to solve it in global case?