Track protos + globals w/out initializers as undef too (not just helper funcs)

Also handle empty global variable lists -- and initialize
ShAddralign to 1 instead of 0, just in case. Previously
it would try to align by 0 when the variable list was empty.

This should help the crosstests pass with --elf.

BUG=none
R=kschimpf@google.com, stichnot@chromium.org

Review URL: https://codereview.chromium.org/899483002
diff --git a/src/IceConverter.cpp b/src/IceConverter.cpp
index 47f8b39..5316db6 100644
--- a/src/IceConverter.cpp
+++ b/src/IceConverter.cpp
@@ -121,9 +121,20 @@
   Ice::Constant *convertConstant(const Constant *Const) {
     if (const auto GV = dyn_cast<GlobalValue>(Const)) {
       Ice::GlobalDeclaration *Decl = getConverter().getGlobalDeclaration(GV);
-      const Ice::RelocOffsetT Offset = 0;
-      return Ctx->getConstantSym(Offset, Decl->getName(),
-                                 Decl->getSuppressMangling());
+      bool IsUndefined = false;
+      if (const auto *Func = llvm::dyn_cast<Ice::FunctionDeclaration>(Decl))
+        IsUndefined = Func->isProto();
+      else if (const auto *Var = llvm::dyn_cast<Ice::VariableDeclaration>(Decl))
+        IsUndefined = !Var->hasInitializer();
+      else
+        report_fatal_error("Unhandled GlobalDeclaration type");
+      if (IsUndefined)
+        return Ctx->getConstantExternSym(Decl->getName());
+      else {
+        const Ice::RelocOffsetT Offset = 0;
+        return Ctx->getConstantSym(Offset, Decl->getName(),
+                                   Decl->getSuppressMangling());
+      }
     } else if (const auto CI = dyn_cast<ConstantInt>(Const)) {
       Ice::Type Ty = convertToIceType(CI->getType());
       return Ctx->getConstantInt(Ty, CI->getSExtValue());
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
index c428c12..2d11eb3 100644
--- a/src/IceELFObjectWriter.cpp
+++ b/src/IceELFObjectWriter.cpp
@@ -308,11 +308,13 @@
 void ELFObjectWriter::writeDataOfType(SectionType ST,
                                       const VariableDeclarationList &Vars,
                                       FixupKind RelocationKind, bool IsELF64) {
+  if (Vars.empty())
+    return;
   ELFDataSection *Section;
   ELFRelocationSection *RelSection;
   // TODO(jvoung): Handle fdata-sections.
   IceString SectionName;
-  Elf64_Xword ShAddralign = 0;
+  Elf64_Xword ShAddralign = 1;
   for (VariableDeclaration *Var : Vars) {
     Elf64_Xword Align = Var->getAlignment();
     ShAddralign = std::max(ShAddralign, Align);
@@ -362,6 +364,10 @@
 
   const uint8_t SymbolType = STT_OBJECT;
   for (VariableDeclaration *Var : Vars) {
+    // If the variable declaration does not have an initializer, its symtab
+    // entry will be created separately.
+    if (!Var->hasInitializer())
+      continue;
     Elf64_Xword Align = Var->getAlignment();
     Section->padToAlignment(Str, Align);
     SizeT SymbolSize = Var->getNumBytes();
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 249875f..4d83578 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -284,10 +284,15 @@
     // TODO(kschimpf) Don't get addresses of intrinsic function declarations.
     Ice::GlobalDeclaration *Decl = nullptr;
     unsigned FcnIDSize = FunctionDeclarationList.size();
+    bool IsUndefined = false;
     if (ID < FcnIDSize) {
       Decl = FunctionDeclarationList[ID];
+      const auto Func = llvm::cast<Ice::FunctionDeclaration>(Decl);
+      IsUndefined = Func->isProto();
     } else if ((ID - FcnIDSize) < VariableDeclarations.size()) {
       Decl = VariableDeclarations[ID - FcnIDSize];
+      const auto Var = llvm::cast<Ice::VariableDeclaration>(Decl);
+      IsUndefined = !Var->hasInitializer();
     }
     std::string Name;
     bool SuppressMangling;
@@ -303,9 +308,13 @@
       Name = "??";
       SuppressMangling = false;
     }
-    const Ice::RelocOffsetT Offset = 0;
-    C = getTranslator().getContext()->getConstantSym(Offset, Name,
-                                                     SuppressMangling);
+    if (IsUndefined)
+      C = getTranslator().getContext()->getConstantExternSym(Name);
+    else {
+      const Ice::RelocOffsetT Offset = 0;
+      C = getTranslator().getContext()->getConstantSym(Offset, Name,
+                                                       SuppressMangling);
+    }
     ValueIDConstants[ID] = C;
     return C;
   }
diff --git a/tests_lit/llvm2ice_tests/elf_container.ll b/tests_lit/llvm2ice_tests/elf_container.ll
index d8b6ad0..125be2c 100644
--- a/tests_lit/llvm2ice_tests/elf_container.ll
+++ b/tests_lit/llvm2ice_tests/elf_container.ll
@@ -21,6 +21,11 @@
 declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
 declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i32, i1)
 
+; Try other external functions (for cross tests).
+; Not testing external global variables since the NaCl bitcode writer
+; refuses to freeze such IR.
+declare void @external_foo(i32)
+
 ; Test some global data relocs (data, rodata, bss).
 @bytes = internal global [7 x i8] c"ab\03\FF\F6fg", align 1
 @bytes_const = internal constant [7 x i8] c"ab\03\FF\F6fg", align 1
@@ -97,6 +102,12 @@
   ret float %f
 }
 
+; Test calling an external function.
+define internal void @test_call_external() {
+  call void @external_foo(i32 42)
+  ret void
+}
+
 ; Test copying a function pointer, or a global data pointer.
 define internal i32 @test_ret_fp() {
   %r = ptrtoint float ()* @returnFloatConst to i32
@@ -387,6 +398,7 @@
 ; CHECK:     0x34 R_386_32 .L$double$2 0x0
 ; CHECK:     0x{{.*}} R_386_PC32 memcpy
 ; CHECK:     0x{{.*}} R_386_PC32 memset
+; CHECK:     0x{{.*}} R_386_PC32 external_foo
 ; CHECK:   }
 ; CHECK:   Section ({{[0-9]+}}) .rel.data {
 ; The set of relocations between llvm-mc and the integrated elf-writer
@@ -614,6 +626,15 @@
 ; CHECK-NEXT:     Section: .text
 ; CHECK-NEXT:   }
 ; CHECK:        Symbol {
+; CHECK:          Name: external_foo
+; CHECK-NEXT:     Value: 0x0
+; CHECK-NEXT:     Size: 0
+; CHECK-NEXT:     Binding: Global
+; CHECK-NEXT:     Type: None
+; CHECK-NEXT:     Other: 0
+; CHECK-NEXT:     Section: Undefined
+; CHECK-NEXT:   }
+; CHECK:        Symbol {
 ; CHECK:          Name: memcpy
 ; CHECK-NEXT:     Value: 0x0
 ; CHECK-NEXT:     Size: 0
diff --git a/tests_lit/llvm2ice_tests/elf_nodata.ll b/tests_lit/llvm2ice_tests/elf_nodata.ll
new file mode 100644
index 0000000..8170676
--- /dev/null
+++ b/tests_lit/llvm2ice_tests/elf_nodata.ll
@@ -0,0 +1,127 @@
+; Tests that we generate an ELF container correctly when there
+; is no data section.
+
+; For the integrated ELF writer, we can't pipe the output because we need
+; to seek backward and patch up the file headers. So, use a temporary file.
+; RUN: %p2i -i %s --args -O2 --verbose none -elf-writer -o %t \
+; RUN:   && llvm-readobj -file-headers -sections -section-data \
+; RUN:       -relocations -symbols %t | FileCheck %s
+
+; RUN: %p2i -i %s --args -O2 --verbose none \
+; RUN:   | llvm-mc -triple=i686-none-nacl -filetype=obj -o - \
+; RUN:   | llvm-readobj -file-headers -sections -section-data \
+; RUN:       -relocations -symbols - | FileCheck %s
+
+declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
+
+define internal i32 @foo(i32 %x, i32 %len) {
+  %y = add i32 %x, %x
+  %dst = inttoptr i32 %y to i8*
+  %src = inttoptr i32 %x to i8*
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src,
+                                       i32 %len, i32 1, i1 false)
+
+  ret i32 %y
+}
+
+; Test defining a non-internal function.
+define void @_start(i32 %x) {
+  %ignored = call i32 @foo(i32 %x, i32 4)
+  ret void
+}
+
+; CHECK:   Section {
+; CHECK:     Index: {{[1-9][0-9]*}}
+; CHECK:     Name: .text
+; CHECK:     Type: SHT_PROGBITS
+; CHECK:     Flags [ (0x6)
+; CHECK:       SHF_ALLOC
+; CHECK:       SHF_EXECINSTR
+; CHECK:     ]
+; CHECK:     Address: 0x0
+; CHECK:     Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK:     Size: {{[1-9][0-9]*}}
+; CHECK:     Link: 0
+; CHECK:     Info: 0
+; CHECK:     AddressAlignment: 32
+; CHECK:     EntrySize: 0
+; CHECK:     SectionData (
+; CHECK:     )
+; CHECK:   }
+; CHECK:   Section {
+; CHECK:     Index: {{[1-9][0-9]*}}
+; CHECK:     Name: .rel.text
+; CHECK:     Type: SHT_REL
+; CHECK:     Flags [ (0x0)
+; CHECK:     ]
+; CHECK:     Address: 0x0
+; CHECK:     Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK:     Size: {{[1-9][0-9]*}}
+; CHECK:     Link: [[SYMTAB_INDEX:[1-9][0-9]*]]
+; CHECK:     Info: {{[1-9][0-9]*}}
+; CHECK:     AddressAlignment: 4
+; CHECK:     EntrySize: 8
+; CHECK:     SectionData (
+; CHECK:     )
+; CHECK:   }
+; CHECK:   Section {
+; CHECK:     Index: [[SYMTAB_INDEX]]
+; CHECK-NEXT: Name: .symtab
+; CHECK:     Type: SHT_SYMTAB
+; CHECK:     Flags [ (0x0)
+; CHECK:     ]
+; CHECK:     Address: 0x0
+; CHECK:     Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK:     Size: {{[1-9][0-9]*}}
+; CHECK:     Link: {{[1-9][0-9]*}}
+; CHECK:     Info: {{[1-9][0-9]*}}
+; CHECK:     AddressAlignment: 4
+; CHECK:     EntrySize: 16
+; CHECK:   }
+
+
+; CHECK: Relocations [
+; CHECK:   Section ({{[0-9]+}}) .rel.text {
+; CHECK:     0x21 R_386_PC32 memcpy 0x0
+; CHECK:   }
+; CHECK: ]
+
+
+; CHECK: Symbols [
+; CHECK-NEXT:   Symbol {
+; CHECK-NEXT:     Name: (0)
+; CHECK-NEXT:     Value: 0x0
+; CHECK-NEXT:     Size: 0
+; CHECK-NEXT:     Binding: Local
+; CHECK-NEXT:     Type: None
+; CHECK-NEXT:     Other: 0
+; CHECK-NEXT:     Section: Undefined (0x0)
+; CHECK-NEXT:   }
+; CHECK:        Symbol {
+; CHECK:          Name: foo
+; CHECK-NEXT:     Value: 0x0
+; CHECK-NEXT:     Size: 0
+; CHECK-NEXT:     Binding: Local
+; CHECK-NEXT:     Type: None
+; CHECK-NEXT:     Other: 0
+; CHECK-NEXT:     Section: .text
+; CHECK-NEXT:   }
+; CHECK:        Symbol {
+; CHECK:          Name: _start
+; CHECK-NEXT:     Value: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK-NEXT:     Size: 0
+; CHECK-NEXT:     Binding: Global
+; CHECK-NEXT:     Type: Function
+; CHECK-NEXT:     Other: 0
+; CHECK-NEXT:     Section: .text
+; CHECK-NEXT:   }
+; CHECK:        Symbol {
+; CHECK:          Name: memcpy
+; CHECK-NEXT:     Value: 0x0
+; CHECK-NEXT:     Size: 0
+; CHECK-NEXT:     Binding: Global
+; CHECK-NEXT:     Type: None
+; CHECK-NEXT:     Other: 0
+; CHECK-NEXT:     Section: Undefined
+; CHECK-NEXT:   }
+; CHECK: ]