Is read and write variables always translated to load and store IR instructions?

Hi,

It seems that read and write access to variables are translated to
load and store IR instructions.

Are they only translated to load and store IR instructions in .ll
code? Could read and write accesses be translated to other IR
instructions (for .0.0.preopt.bc only)? Thanks.

$ clang -std=gnu99 -g3 -flto -Wall -pedantic -c -o main.o main.c
$ clang main.o -flto -fuse-ld=gold '-Wl,-plugin-opt=save-temps' -o main.exe
$ cat main.c
/* vim: set noexpandtab tabstop=2: */
#include <stdio.h>

int i;

struct {
  int x;
  int y;
} a[10];

void print() {
  puts("Hello World!");
}

int main(int argc, char *argv[]) {
  ++i;
  if(i) {
    ++a[0].x;
    print();
    printf("%d\n", i);
    printf("%d\n", a[0].x);
  }
  return 0;
}
$ cat main.exe.0.0.preopt.ll
source_filename = "ld-temp.o"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%struct.anon = type { i32, i32 }

@.str = private unnamed_addr constant [13 x i8] c"Hello World!\00", align 1
@i = common dso_local global i32 0, align 4, !dbg !0
@a = common dso_local global [10 x %struct.anon] zeroinitializer,
align 16, !dbg !6
@.str.1 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @print() #0 !dbg !20 {
  %1 = call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x
i8]* @.str, i32 0, i32 0)), !dbg !23
  ret void, !dbg !24
}

declare i32 @puts(i8*) #1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main(i32, i8**) #0 !dbg !25 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  call void @llvm.dbg.declare(metadata i32* %4, metadata !31, metadata
!DIExpression()), !dbg !32
  store i8** %1, i8*** %5, align 8
  call void @llvm.dbg.declare(metadata i8*** %5, metadata !33,
metadata !DIExpression()), !dbg !34
  %6 = load i32, i32* @i, align 4, !dbg !35
  %7 = add nsw i32 %6, 1, !dbg !35
  store i32 %7, i32* @i, align 4, !dbg !35
  %8 = load i32, i32* @i, align 4, !dbg !36
  %9 = icmp ne i32 %8, 0, !dbg !36
  br i1 %9, label %10, label %17, !dbg !38

; <label>:10: ; preds = %2
  %11 = load i32, i32* getelementptr inbounds ([10 x %struct.anon],
[10 x %struct.anon]* @a, i64 0, i64 0, i32 0), align 16, !dbg !39
  %12 = add nsw i32 %11, 1, !dbg !39
  store i32 %12, i32* getelementptr inbounds ([10 x %struct.anon], [10
x %struct.anon]* @a, i64 0, i64 0, i32 0), align 16, !dbg !39
  call void @print(), !dbg !41
  %13 = load i32, i32* @i, align 4, !dbg !42
  %14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x
i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %13), !dbg !43
  %15 = load i32, i32* getelementptr inbounds ([10 x %struct.anon],
[10 x %struct.anon]* @a, i64 0, i64 0, i32 0), align 16, !dbg !44
  %16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x
i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %15), !dbg !45
  br label %17, !dbg !46

; <label>:17: ; preds = %10, %2
  ret i32 0, !dbg !47
}

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

declare i32 @printf(i8*, ...) #1

attributes #0 = { noinline nounwind optnone uwtable
"correctly-rounded-divide-sqrt-fp-math"="false"
"disable-tail-calls"="false" "less-precise-fpmad"="false"
"no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"
"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"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87"
"unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false"
"disable-tail-calls"="false" "less-precise-fpmad"="false"
"no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"
"no-infs-fp-math"="false" "no-nans-fp-math"="false"
"no-signed-zeros-fp-math"="false" "no-trapping-math"="false"
"stack-protector-buffer-size"="8" "target-cpu"="x86-64"
"target-features"="+fxsr,+mmx,+sse,+sse2,+x87"
"unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind readnone speculatable }

!llvm.dbg.cu = !{!2}
!llvm.ident = !{!16}
!llvm.module.flags = !{!17, !18, !19}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "i", scope: !2, file: !3, line:
4, type: !12, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3,
producer: "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)",
isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums:
!4, globals: !5)
!4 = !{}
!5 = !{!0, !6}
!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
!7 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line:
9, type: !8, isLocal: false, isDefinition: true)
!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 640,
elements: !14)
!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3,
line: 6, size: 64, elements: !10)
!10 = !{!11, !13}
!11 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !9, file:
!3, line: 7, baseType: !12, size: 32)
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !9, file:
!3, line: 8, baseType: !12, size: 32, offset: 32)
!14 = !{!15}
!15 = !DISubrange(count: 10)
!16 = !{!"clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)"}
!17 = !{i32 2, !"Dwarf Version", i32 4}
!18 = !{i32 2, !"Debug Info Version", i32 3}
!19 = !{i32 1, !"wchar_size", i32 4}
!20 = distinct !DISubprogram(name: "print", scope: !3, file: !3, line:
11, type: !21, isLocal: false, isDefinition: true, scopeLine: 11,
isOptimized: false, unit: !2, retainedNodes: !4)
!21 = !DISubroutineType(types: !22)
!22 = !{null}
!23 = !DILocation(line: 12, column: 3, scope: !20)
!24 = !DILocation(line: 13, column: 1, scope: !20)
!25 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line:
15, type: !26, isLocal: false, isDefinition: true, scopeLine: 15,
flags: DIFlagPrototyped, isOptimized: false, unit: !2, retainedNodes:
!4)
!26 = !DISubroutineType(types: !27)
!27 = !{!12, !12, !28}
!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64)
!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64)
!30 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!31 = !DILocalVariable(name: "argc", arg: 1, scope: !25, file: !3,
line: 15, type: !12)
!32 = !DILocation(line: 15, column: 14, scope: !25)
!33 = !DILocalVariable(name: "argv", arg: 2, scope: !25, file: !3,
line: 15, type: !28)
!34 = !DILocation(line: 15, column: 26, scope: !25)
!35 = !DILocation(line: 16, column: 3, scope: !25)
!36 = !DILocation(line: 17, column: 6, scope: !37)
!37 = distinct !DILexicalBlock(scope: !25, file: !3, line: 17, column: 6)
!38 = !DILocation(line: 17, column: 6, scope: !25)
!39 = !DILocation(line: 18, column: 5, scope: !40)
!40 = distinct !DILexicalBlock(scope: !37, file: !3, line: 17, column: 9)
!41 = !DILocation(line: 19, column: 5, scope: !40)
!42 = !DILocation(line: 20, column: 20, scope: !40)
!43 = !DILocation(line: 20, column: 5, scope: !40)
!44 = !DILocation(line: 21, column: 25, scope: !40)
!45 = !DILocation(line: 21, column: 5, scope: !40)
!46 = !DILocation(line: 22, column: 3, scope: !40)
!47 = !DILocation(line: 23, column: 3, scope: !25)

Hi,

It seems that read and write access to variables are translated to
load and store IR instructions.

Are they only translated to load and store IR instructions in .ll
code? Could read and write accesses be translated to other IR
instructions (for .0.0.preopt.bc only)? Thanks.

I think this is more a question for cfe-dev since llvm-dev only really
gets involved once the IR has been generated.

That said, a proper answer probably involves some quite involved
discussions about just what counts as an access. At the very least
Clang will also generate calls to @llvm.memcpy.* for struct copies.
For example:

    typedef struct {
      int a;
    } Foo;

    void func(Foo f) {
      Foo g = f;
    }

will cause Clang to initialize 'g' with a memcpy. memset can be
generated similarly if you set a struct to 0.

Cheers.

Tim.