gollvm: use non-integral pointers for GC

When the GC support is enabled, the statepoint insertion pass
needs to analyze the function IR and finds all the live
pointers. It is important to preserve the pointerness in the
optimization passes.

LLVM provides a mechanism, non-integral pointers, which prevents
the optimizer to rewrite pointer operations to integer
operations. We make use of this in CL.

In LLVM, non-integral pointers are pointers in non-integral
address spaces. The default address space 0 cannot be non-
integral, so we use address space 1.

There are some trickiness around the address spaces in LLVM.
For example, although stacks (allocas) are technically allowed
to be in non-zero address space, things fall apart in various
places in the backend. Therefore, currently we use the following
design:

- All Go pointer types are in address space 1.
- Heap pointers are in address space 1.
- Global variables are in address space 1, so the addresses of
  globals are non-integral pointers and can be assigned to
  pointer variables.
- Stacks (allocas) are in address space 0. For each stack
  variable, we create an alloca in address space 0, immediately
  cast to address space 1, using this value as the variable's
  address. This way, we can assign a stack variable's address to
  pointer variables.
- Program text is also in address space 0. When we take the
  address of function code, we cast it to address space 1, so it
  is assignable to pointer variables. We don't need address space
  casts in direct calls.

In LLVM it is illegal to cast non-integral pointers to/from
integers. However, Go supports pointer<->integer casts. These
casts are handled with address space 0 as an intermediate, i.e.

addrspace(1) pointer <-> addrspace(0) pointer <-> integer

It seems that LLVM code generator does not handle addrspacecast
in static initializers. So we add a IR pass that runs after the
statepoint insertion pass, removes addrspacecast in static
initializers. At that point we don't need non-integral pointers
anymore, so we remove the non-integral constraint there, which
makes the code generator happy.

Change-Id: I26e0cff874e1dfdfbccd89fc7d3c7c190fbae057
Reviewed-on: https://go-review.googlesource.com/c/152001
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/bridge/go-llvm-bfunction.cpp b/bridge/go-llvm-bfunction.cpp
index 7382331..e3fbe6b 100644
--- a/bridge/go-llvm-bfunction.cpp
+++ b/bridge/go-llvm-bfunction.cpp
@@ -81,6 +81,15 @@
   if (! name.empty())
     inst->setName(name);
   allocas_.push_back(inst);
+
+  // Immediately cast to address space
+  unsigned addressSpace = abiOracle_->tm()->addressSpace();
+  if (addressSpace != 0) {
+    llvm::Type *pt = llvm::PointerType::get(typ, addressSpace);
+    inst = new llvm::AddrSpaceCastInst(inst, pt);
+    allocas_.push_back(inst);
+  }
+
   return inst;
 }
 
@@ -251,8 +260,12 @@
   } else {
     inst = addAlloca(btype->type(), name);
   }
-  if (is_address_taken)
-    inst->setMetadata("go_addrtaken", llvm::MDNode::get(inst->getContext(), {}));
+  if (is_address_taken) {
+    llvm::Instruction *alloca = inst;
+    if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(alloca))
+      alloca = llvm::cast<llvm::Instruction>(ascast->getPointerOperand());
+    alloca->setMetadata("go_addrtaken", llvm::MDNode::get(inst->getContext(), {}));
+  }
   Bvariable *bv =
       new Bvariable(btype, location, name, LocalVar, is_address_taken, inst);
   localVariables_.push_back(bv);
@@ -468,7 +481,7 @@
 }
 
 void Bfunction::fixupProlog(llvm::BasicBlock *entry,
-                            const std::vector<llvm::AllocaInst *> &temps)
+                            const std::vector<llvm::Instruction *> &temps)
 {
   lazyAbiSetup();
   // If there are any "new" temporaries discovered during the control
@@ -477,8 +490,14 @@
   // (potentially) references to the alloca instructions themselves, so
   // we insert any new temps into the start of the block.
   if (! temps.empty())
-    for (auto ai : temps)
+    for (auto ai : temps) {
       entry->getInstList().push_front(ai);
+      if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(ai)) {
+        llvm::Value *op = ascast->getPointerOperand();
+        assert(llvm::isa<llvm::AllocaInst>(op));
+        entry->getInstList().push_front(llvm::cast<llvm::Instruction>(op));
+      }
+    }
 }
 
 llvm::Value *Bfunction::genReturnSequence(Bexpression *toRet,
diff --git a/bridge/go-llvm-bfunction.h b/bridge/go-llvm-bfunction.h
index f6d67e7..f83aafa 100644
--- a/bridge/go-llvm-bfunction.h
+++ b/bridge/go-llvm-bfunction.h
@@ -108,7 +108,7 @@
   // the the entry BB for the function, and "temps" is a set of temporary
   // variables that need to be adopted into the function.
   void fixupProlog(llvm::BasicBlock *entry,
-                   const std::vector<llvm::AllocaInst *> &temps);
+                   const std::vector<llvm::Instruction *> &temps);
 
   // Create code to return a function value from this fcn, following ABI rules.
   llvm::Value *genReturnSequence(Bexpression *toRet,
diff --git a/bridge/go-llvm-bnode.cpp b/bridge/go-llvm-bnode.cpp
index e0e6290..9e6fd9a 100644
--- a/bridge/go-llvm-bnode.cpp
+++ b/bridge/go-llvm-bnode.cpp
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "go-llvm.h"
 #include "go-llvm-btype.h"
 #include "go-llvm-bnode.h"
 #include "go-llvm-bvariable.h"
@@ -372,6 +373,7 @@
 BnodeBuilder::BnodeBuilder(Llvm_backend *be)
     : integrityVisitor_(new IntegrityVisitor(be, TreeIntegCtl(DumpPointers, DontReportRepairableSharing, IncrementalMode)))
     , checkIntegrity_(true)
+    , addressSpace_(be->addressSpace())
 {
 }
 
@@ -398,14 +400,21 @@
       delete expr;
     }
   }
-  for (auto inst : todel)
-    inst->deleteValue();
-  for (auto inst : deadInstructions_)
-    inst->deleteValue();
   for (auto ivpair : tempvars_) {
-    delete ivpair.first;
+    llvm::Instruction *inst = ivpair.first;
+    if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(inst)) {
+      inst = llvm::cast<llvm::Instruction>(ascast->getPointerOperand());
+      ascast->dropAllReferences();
+      todel.push_back(ascast);
+    }
+    assert(llvm::isa<llvm::AllocaInst>(inst));
+    todel.push_back(inst);
     delete ivpair.second;
   }
+  for (auto v : todel)
+    v->deleteValue();
+  for (auto inst : deadInstructions_)
+    inst->deleteValue();
 }
 
 void BnodeBuilder::freeStmts()
@@ -509,16 +518,24 @@
                                    const std::string &name)
 {
   assert(varType);
-  llvm::AllocaInst *inst = new llvm::AllocaInst(varType->type(), 0);
+  llvm::Type *typ = varType->type();
+  llvm::Instruction *inst = new llvm::AllocaInst(typ, 0);
   if (! name.empty())
     inst->setName(name);
+
+  // Immediately cast to address space
+  if (addressSpace_ != 0) {
+    llvm::Type *pt = llvm::PointerType::get(typ, addressSpace_);
+    inst = new llvm::AddrSpaceCastInst(inst, pt);
+  }
+
   Bvariable *tvar = new Bvariable(varType, loc, name, LocalVar, true, inst);
   tempvars_[inst] = tvar;
   tvar->markAsTemporary();
   return tvar;
 }
 
-Bvariable *BnodeBuilder::adoptTemporaryVariable(llvm::AllocaInst *alloca)
+Bvariable *BnodeBuilder::adoptTemporaryVariable(llvm::Instruction *alloca)
 {
   assert(alloca);
   auto mit = tempvars_.find(alloca);
@@ -729,6 +746,15 @@
   return archive(rval);
 }
 
+static bool isAlloca(llvm::Value *val)
+{
+  if (llvm::isa<llvm::AllocaInst>(val))
+    return true;
+  if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(val))
+    return llvm::isa<llvm::AllocaInst>(ascast->getPointerOperand());
+  return false;
+}
+
 Bexpression *BnodeBuilder::mkCall(Btype *btype,
                                   llvm::Value *val,
                                   Bfunction *caller,
@@ -751,7 +777,7 @@
       found = true;
     rval->appendInstruction(inst);
   }
-  if (!found && val && !llvm::isa<llvm::AllocaInst>(val))
+  if (!found && val && !isAlloca(val))
     appendInstIfNeeded(rval, val);
   rval->u.func = caller;
   return archive(rval);
diff --git a/bridge/go-llvm-bnode.h b/bridge/go-llvm-bnode.h
index 87a72be..ca63b7f 100644
--- a/bridge/go-llvm-bnode.h
+++ b/bridge/go-llvm-bnode.h
@@ -340,7 +340,7 @@
   // construction (via the mkTempVar method above). If so, the
   // variable is extracted from the builder's set and returned. If the
   // var is not an unadopted temp, then NULL is returned.
-  Bvariable *adoptTemporaryVariable(llvm::AllocaInst *alloca);
+  Bvariable *adoptTemporaryVariable(llvm::Instruction *alloca);
 
   // Free up this expr or stmt (it is garbage). Does not free up children.
   void freeNode(Bnode *node);
@@ -405,10 +405,11 @@
   std::vector<Bstatement *> sarchive_;
   std::vector<SwitchDescriptor*> swcases_;
   std::vector< std::vector<unsigned long> > indexvecs_;
-  std::unordered_map<llvm::AllocaInst*, Bvariable*> tempvars_;
+  std::unordered_map<llvm::Instruction*, Bvariable*> tempvars_;
   std::unique_ptr<IntegrityVisitor> integrityVisitor_;
   bool checkIntegrity_;
   std::vector<llvm::Instruction*> deadInstructions_;
+  unsigned addressSpace_;
 };
 
 // This class helps automate walking of a Bnode subtree; it invokes
diff --git a/bridge/go-llvm-materialize.cpp b/bridge/go-llvm-materialize.cpp
index b09a70e..49c6238 100644
--- a/bridge/go-llvm-materialize.cpp
+++ b/bridge/go-llvm-materialize.cpp
@@ -207,24 +207,45 @@
   // uintptr (as part of GC symbol initializer creation), and in other
   // places in FE-generated code (ex: array index checks).
   if (valType->isPointerTy() && toType == llvmIntegerType()) {
+    if (val->getType()->getPointerAddressSpace() != 0) {
+      // We are using non-integral pointer. Cast to address space 0
+      // before casting to int.
+      llvm::Type *et = val->getType()->getPointerElementType();
+      llvm::Type *pt = llvm::PointerType::get(et, 0);
+      std::string tname(namegen("ascast"));
+      val = builder.CreateAddrSpaceCast(val, pt, tname);
+      expr = nbuilder_.mkConversion(type, val, expr, location);
+    }
     std::string tname(namegen("pticast"));
     llvm::Value *pticast = builder.CreatePtrToInt(val, toType, tname);
     rval = nbuilder_.mkConversion(type, pticast, expr, location);
   }
 
-  // Pointer-sized-integer type pointer type. This comes up
+  // Pointer-sized-integer type to pointer type. This comes up
   // in type hash/compare functions.
   if (toType->isPointerTy() && valType == llvmIntegerType()) {
+    llvm::Type *pt = toType;
+    if (toType->getPointerAddressSpace() != 0) {
+      // We are using non-integral pointer. Cast to address space 0
+      // first.
+      llvm::Type *et = toType->getPointerElementType();
+      pt = llvm::PointerType::get(et, 0);
+    }
     std::string tname(namegen("itpcast"));
-    llvm::Value *itpcast = builder.CreateIntToPtr(val, toType, tname);
+    llvm::Value *itpcast = builder.CreateIntToPtr(val, pt, tname);
     rval = nbuilder_.mkConversion(type, itpcast, expr, location);
+    if (pt != toType) {
+      std::string tname(namegen("ascast"));
+      llvm::Value *ascast = builder.CreateAddrSpaceCast(itpcast, toType, tname);
+      rval = nbuilder_.mkConversion(type, ascast, rval, location);
+    }
   }
 
   // For pointer conversions (ex: *int32 => *int64) create an
   // appropriate bitcast.
   if (valType->isPointerTy() && toType->isPointerTy()) {
     std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder.CreateBitCast(val, toType, tag);
+    llvm::Value *bitcast = builder.CreatePointerBitCastOrAddrSpaceCast(val, toType, tag);
     rval = nbuilder_.mkConversion(type, bitcast, expr, location);
   }
 
@@ -1482,7 +1503,8 @@
   // descriptor. Allow this case.
   if (dstPtrToFD && srcFuncPtr) {
     std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
+    llvm::Value *bitcast =
+        builder->CreatePointerBitCastOrAddrSpaceCast(srcVal, dstToType, tag);
     return bitcast;
   }
 
@@ -1523,7 +1545,7 @@
   if ((dstToType == llvmPtrType() && llvm::isa<llvm::PointerType>(srcType)) ||
       (srcType == llvmPtrType() && llvm::isa<llvm::PointerType>(dstToType))) {
     std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
+    llvm::Value *bitcast = builder->CreatePointerBitCastOrAddrSpaceCast(srcVal, dstToType, tag);
     return bitcast;
   }
 
diff --git a/bridge/go-llvm-typemanager.cpp b/bridge/go-llvm-typemanager.cpp
index ee97280..7047684 100644
--- a/bridge/go-llvm-typemanager.cpp
+++ b/bridge/go-llvm-typemanager.cpp
@@ -22,11 +22,13 @@
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/Type.h"
 
-TypeManager::TypeManager(llvm::LLVMContext &context, llvm::CallingConv::ID conv)
+TypeManager::TypeManager(llvm::LLVMContext &context,
+                         llvm::CallingConv::ID conv,
+                         unsigned addrspace)
     : context_(context)
     , datalayout_(nullptr)
     , cconv_(conv)
-    , addressSpace_(0)
+    , addressSpace_(addrspace)
     , traceLevel_(0)
     , nametags_(nullptr)
     , errorExpression_(nullptr)
@@ -36,6 +38,7 @@
     , llvmVoidType_(nullptr)
     , llvmBoolType_(nullptr)
     , llvmPtrType_(nullptr)
+    , llvmPtr0Type_(nullptr)
     , llvmSizeType_(nullptr)
     , llvmIntegerType_(nullptr)
     , llvmInt8Type_(nullptr)
@@ -56,6 +59,8 @@
   llvmPtrType_ =
       llvm::PointerType::get(llvm::IntegerType::get(context_, 8),
                              addressSpace_);
+  llvmPtr0Type_ =
+      llvm::PointerType::get(llvm::IntegerType::get(context_, 8), 0);
 
   // Assorted pre-computer types for use in builtin function creation
   llvmVoidType_ = llvm::Type::getVoidTy(context_);
@@ -483,6 +488,11 @@
 
 Btype *TypeManager::pointerType(Btype *toType)
 {
+  return addrSpacePointerType(toType, addressSpace_);
+}
+
+Btype *TypeManager::addrSpacePointerType(Btype *toType, unsigned addressSpace)
+{
   if (toType == errorType_)
     return errorType_;
 
@@ -491,7 +501,7 @@
   // as pointer to char.
   llvm::Type *lltot =
       (toType->type() == llvmVoidType_ ? llvmInt8Type_ : toType->type());
-  llvm::Type *llpt = llvm::PointerType::get(lltot, addressSpace_);
+  llvm::Type *llpt = llvm::PointerType::get(lltot, addressSpace);
 
   // Check for and return existing anon type if we have one already
   Location loc;
@@ -1340,7 +1350,7 @@
 llvm::Type *TypeManager::landingPadExceptionType()
 {
   llvm::SmallVector<llvm::Type *, 2> elems(2);
-  elems[0] = llvmPtrType();
+  elems[0] = llvmPtr0Type();
   elems[1] = llvmInt32Type();
   return llvm::StructType::get(context_, elems);
 }
diff --git a/bridge/go-llvm-typemanager.h b/bridge/go-llvm-typemanager.h
index c3e0b1b..8eca95e 100644
--- a/bridge/go-llvm-typemanager.h
+++ b/bridge/go-llvm-typemanager.h
@@ -42,7 +42,9 @@
 
 class TypeManager {
  public:
-  TypeManager(llvm::LLVMContext &context, llvm::CallingConv::ID cconv);
+  TypeManager(llvm::LLVMContext &context,
+              llvm::CallingConv::ID cconv,
+              unsigned addrspace);
   ~TypeManager();
 
   // These methods are intended to match up with the similarly
@@ -114,6 +116,7 @@
   llvm::Type *llvmVoidType() const { return llvmVoidType_; }
   llvm::Type *llvmBoolType() const { return llvmBoolType_; }
   llvm::Type *llvmPtrType() const { return llvmPtrType_; }
+  llvm::Type *llvmPtr0Type() const { return llvmPtr0Type_; } // pointer in address space 0
   llvm::Type *llvmInt8Type() const { return llvmInt8Type_; }
   llvm::Type *llvmInt32Type() const { return llvmInt32Type_; }
   llvm::Type *llvmInt64Type() const { return llvmInt64Type_; }
@@ -147,6 +150,9 @@
   llvm::LLVMContext &context() const { return context_; }
   unsigned addressSpace() const { return addressSpace_; }
 
+  // Same as pointerType, but explicitly specify address space.
+  Btype *addrSpacePointerType(Btype *toType, unsigned addressSpace);
+
   // Go string type
   Btype *stringType() const { return stringType_; }
 
@@ -357,6 +363,7 @@
   llvm::Type *llvmVoidType_;
   llvm::Type *llvmBoolType_;
   llvm::Type *llvmPtrType_;
+  llvm::Type *llvmPtr0Type_;
   llvm::Type *llvmSizeType_;
   llvm::Type *llvmIntegerType_;
   llvm::Type *llvmInt8Type_;
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 3751410..ac6eafb 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -42,14 +42,15 @@
 
 Llvm_backend::Llvm_backend(llvm::LLVMContext &context,
                            llvm::Module *module,
-                           Llvm_linemap *linemap)
-    : TypeManager(context, llvm::CallingConv::X86_64_SysV)
+                           Llvm_linemap *linemap,
+                           unsigned addrspace)
+    : TypeManager(context, llvm::CallingConv::X86_64_SysV, addrspace)
     , context_(context)
     , module_(module)
     , datalayout_(module ? &module->getDataLayout() : nullptr)
     , nbuilder_(this)
     , linemap_(linemap)
-    , addressSpace_(0)
+    , addressSpace_(addrspace)
     , traceLevel_(0)
     , noInline_(false)
     , noFpElim_(false)
@@ -747,7 +748,7 @@
   // apply a bitcast to perform the conversion.
   LIRBuilder builder(context_, llvm::ConstantFolder());
   if (! isToComposite) {
-    llvm::Value *bitcast = builder.CreateBitCast(fromVal, llToType);
+    llvm::Value *bitcast = builder.CreatePointerBitCastOrAddrSpaceCast(fromVal, llToType);
     assert(llvm::isa<llvm::Constant>(bitcast));
     llvm::Constant *constCast = llvm::cast<llvm::Constant>(bitcast);
     return constCast;
@@ -1432,13 +1433,16 @@
   if (bfunc == errorFunction_.get())
     return errorExpression();
 
-  // Look up pointer-to-function type
-  Btype *fpBtype = pointer_type(bfunc->fcnType());
+  // Look up pointer-to-function type.
+  // Function is always in address space 0.
+  Btype *fpBtype = addrSpacePointerType(bfunc->fcnType(), 0);
+
+  llvm::Constant *fpval = bfunc->fcnValue();
 
   // Create an address-of-function expr
-  Bexpression *fexpr = nbuilder_.mkFcnAddress(fpBtype, bfunc->fcnValue(),
+  Bexpression *fexpr = nbuilder_.mkFcnAddress(fpBtype, fpval,
                                               bfunc, location);
-  return makeGlobalExpression(fexpr, bfunc->fcnValue(), fpBtype, location);
+  return makeGlobalExpression(fexpr, fpval, fpBtype, location);
 }
 
 // Return an expression for the field at INDEX in BSTRUCT.
@@ -2128,9 +2132,12 @@
     }
   }
 
+  llvm::GlobalValue::ThreadLocalMode threadlocal =
+      llvm::GlobalValue::NotThreadLocal;
   glob = new llvm::GlobalVariable(module(), underlyingType->type(),
                                   isConstant == MV_Constant,
-                                  linkage, init, gname);
+                                  linkage, init, gname, nullptr,
+                                  threadlocal, addressSpace_);
 
   if (alignment)
     glob->setAlignment(alignment);
@@ -2454,8 +2461,11 @@
   llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage;
   bool isConstant = true;
   llvm::Constant *init = nullptr;
+  llvm::GlobalValue::ThreadLocalMode threadlocal =
+      llvm::GlobalValue::NotThreadLocal;
   glob = new llvm::GlobalVariable(module(), btype->type(), isConstant,
-                                  linkage, init, gname);
+                                  linkage, init, gname, nullptr,
+                                  threadlocal, addressSpace_);
 
   Bvariable *bv =
       new Bvariable(btype, location, name, GlobalVar, false, glob);
@@ -2769,8 +2779,8 @@
   std::vector<Bblock *> blockStack_;
   std::map<LabelId, llvm::BasicBlock *> labelmap_;
   std::vector<llvm::BasicBlock*> padBlockStack_;
-  std::set<llvm::AllocaInst *> temporariesDiscovered_;
-  std::vector<llvm::AllocaInst *> newTemporaries_;
+  std::set<llvm::Instruction *> temporariesDiscovered_;
+  std::vector<llvm::Instruction *> newTemporaries_;
   llvm::BasicBlock *finallyBlock_;
   Bstatement *cachedReturn_;
 };
@@ -2829,6 +2839,8 @@
   else
     builder.SetInsertPoint(curBlock);
 
+  if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(alloca))
+    alloca = ascast->getPointerOperand();
   llvm::AllocaInst *ai = llvm::cast<llvm::AllocaInst>(alloca);
 
   // expect array size of 1 given how we constructed it
@@ -2970,8 +2982,9 @@
 {
   for (llvm::Instruction::op_iterator oi = inst->op_begin(),
            oe = inst->op_end(); oi != oe; ++oi) {
-    if (llvm::isa<llvm::AllocaInst>(*oi)) {
-      llvm::AllocaInst *ai = llvm::cast<llvm::AllocaInst>(*oi);
+    if (llvm::isa<llvm::AllocaInst>(*oi) ||
+        llvm::isa<llvm::AddrSpaceCastInst>(*oi)) {
+      llvm::Instruction *ai = llvm::cast<llvm::Instruction>(*oi);
 
       // If this alloca is associated with a temporary variable
       // that was manufactured at some point during IR construction,
@@ -3366,7 +3379,7 @@
 
   // Create temporary into which caught result will be stored
   std::string tag(be_->namegen("ehtmp"));
-  llvm::AllocaInst *ai = new llvm::AllocaInst(eht, 0, tag);
+  llvm::Instruction *ai = new llvm::AllocaInst(eht, 0, tag);
   temporariesDiscovered_.insert(ai);
   newTemporaries_.push_back(ai);
 
@@ -3854,5 +3867,5 @@
 // Return a new backend generator.
 
 Backend *go_get_backend(llvm::LLVMContext &context) {
-  return new Llvm_backend(context, nullptr, nullptr);
+  return new Llvm_backend(context, nullptr, nullptr, 0);
 }
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 67ac52b..382934c 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -78,7 +78,8 @@
 public:
   Llvm_backend(llvm::LLVMContext &context,
                llvm::Module *module,
-               Llvm_linemap *linemap);
+               Llvm_linemap *linemap,
+               unsigned addrspace);
   ~Llvm_backend();
 
   // Types.
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 62ddfd6..23fc08c 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -462,19 +462,30 @@
   context_.setDiagnosticHandler(
       llvm::make_unique<BEDiagnosticHandler>(&this->hasError_));
 
+  llvm::Optional<unsigned> enable_gc =
+      driver_.getLastArgAsInteger(gollvm::options::OPT_enable_gc_EQ, 0u);
+  enable_gc_ = enable_gc && *enable_gc;
+
   // Construct linemap and module
   linemap_.reset(new Llvm_linemap());
   module_.reset(new llvm::Module("gomodule", context_));
 
   // Add the target data from the target machine, if it exists
   module_->setTargetTriple(triple_.getTriple());
-  module_->setDataLayout(target_->createDataLayout());
+
+  // Data layout.
+  std::string dlstr = target_->createDataLayout().getStringRepresentation();
+  if (enable_gc_)
+    dlstr += "-ni:1"; // non-integral pointer in address space 1
+  module_->setDataLayout(dlstr);
+
   module_->setPICLevel(driver_.getPicLevel());
   if (driver_.picIsPIE())
     module_->setPIELevel(driver_.getPieLevel());
 
   // Now construct Llvm_backend helper.
-  bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get()));
+  unsigned addrspace = enable_gc_ ? 1 : 0;
+  bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get(), addrspace));
 
   // Honor inline, tracelevel cmd line options
   llvm::Optional<unsigned> tl =
@@ -504,14 +515,13 @@
   for (const auto &arg : driver_.args().getAllArgValues(gollvm::options::OPT_fdebug_prefix_map_EQ))
     bridge_->addDebugPrefix(llvm::StringRef(arg).split('='));
 
-  llvm::Optional<unsigned> enable_gc =
-      driver_.getLastArgAsInteger(gollvm::options::OPT_enable_gc_EQ, 0u);
-  if (enable_gc && *enable_gc) {
-    enable_gc_ = true;
+  // GC support.
+  if (enable_gc_) {
     bridge_->setGCStrategy("go");
     linkGoGC();
     linkGoGCPrinter();
   }
+
   // Support -fgo-dump-ast
   if (args_.hasArg(gollvm::options::OPT_fgo_dump_ast))
     go_enable_dump("ast");
@@ -753,8 +763,10 @@
 
   // Add statepoint insertion pass to the end of optimization pipeline,
   // right before lowering to machine IR.
-  if (enable_gc_)
+  if (enable_gc_) {
     modulePasses.add(createGoStatepointsLegacyPass());
+    modulePasses.add(createRemoveAddrSpacePass(target_->createDataLayout()));
+  }
 
   legacy::PassManager codeGenPasses;
   bool noverify = args_.hasArg(gollvm::options::OPT_noverify);
diff --git a/passes/CMakeLists.txt b/passes/CMakeLists.txt
index 2919b57..1a65572 100644
--- a/passes/CMakeLists.txt
+++ b/passes/CMakeLists.txt
@@ -10,5 +10,6 @@
   GC.cpp
   GoAnnotation.cpp
   GoStatepoints.cpp
+  RemoveAddrSpace.cpp
   Util.cpp
 )
diff --git a/passes/GollvmPasses.h b/passes/GollvmPasses.h
index 2691c67..e388b1f 100644
--- a/passes/GollvmPasses.h
+++ b/passes/GollvmPasses.h
@@ -22,9 +22,11 @@
 
 void initializeGoAnnotationPass(PassRegistry&);
 void initializeGoStatepointsLegacyPassPass(PassRegistry&);
+void initializeRemoveAddrSpacePassPass(PassRegistry&);
 
 FunctionPass *createGoAnnotationPass();
 ModulePass *createGoStatepointsLegacyPass();
+ModulePass *createRemoveAddrSpacePass(const DataLayout&);
 
 void linkGoGC();
 void linkGoGCPrinter();
diff --git a/passes/RemoveAddrSpace.cpp b/passes/RemoveAddrSpace.cpp
new file mode 100644
index 0000000..e31e9ac
--- /dev/null
+++ b/passes/RemoveAddrSpace.cpp
@@ -0,0 +1,99 @@
+//===--- GoStackMap.cpp ---------------------------------------------------===//
+//
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+//===----------------------------------------------------------------------===//
+//
+// LLVM backend pass to remove addrspacecast instructions
+// in static initializers (because codegen cannot handle
+// them).
+//
+//===----------------------------------------------------------------------===//
+
+#include "GollvmPasses.h"
+
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Pass.h"
+#include "llvm/PassRegistry.h"
+
+using namespace llvm;
+
+namespace {
+
+class RemoveAddrSpacePass : public ModulePass {
+ public:
+  static char ID;
+
+  RemoveAddrSpacePass() : ModulePass(ID), DL("") {}
+
+  RemoveAddrSpacePass(const DataLayout &DL) : ModulePass(ID), DL(DL) {
+    initializeRemoveAddrSpacePassPass(
+        *PassRegistry::getPassRegistry());
+  }
+
+  bool runOnModule(Module &M) override;
+
+ private:
+  Type *IntTy; // type of uintptr_t, used to cast pointer to/from integer
+  DataLayout DL; // data layout without non-integral pointer, passed in from the driver
+  DenseSet<Constant*> Visited; // handle circular references
+
+  // Replace addrspacecast in static initializer C with a pair
+  // of ptr-to-int and int-to-ptr casts, as codegen cannot handle
+  // addrspacecast in static initializer.
+  void removeAddrSpaceCast(Constant *C);
+};
+
+} // namespace
+
+char RemoveAddrSpacePass::ID = 0;
+INITIALIZE_PASS(RemoveAddrSpacePass, "remove-addrspacecast",
+                "Remove addrspacecast instructions", false,
+                false)
+ModulePass *llvm::createRemoveAddrSpacePass(const DataLayout &DL) {
+  return new RemoveAddrSpacePass(DL);
+}
+
+void
+RemoveAddrSpacePass::removeAddrSpaceCast(Constant *C) {
+  if (Visited.count(C))
+    return;
+  Visited.insert(C);
+
+  ConstantExpr *CE = dyn_cast<ConstantExpr>(C);
+  if (CE && CE->getOpcode() == Instruction::AddrSpaceCast) {
+    Constant *Op = CE->getOperand(0);
+    Constant *New = ConstantExpr::getIntToPtr(
+        ConstantExpr::getPtrToInt(Op, IntTy), CE->getType());
+    CE->replaceAllUsesWith(New);
+    Visited.erase(CE);
+    CE->destroyConstant();
+    C = New;
+  }
+
+  unsigned N = C->getNumOperands();
+  for (unsigned Idx = 0; Idx < N; Idx++) {
+    Constant *Op = cast<Constant>(C->getOperand(Idx));
+    removeAddrSpaceCast(Op);
+  }
+}
+
+bool
+RemoveAddrSpacePass::runOnModule(Module &M) {
+  // At this point we no longer need non-integral pointers.
+  // Set data layout back to default.
+  M.setDataLayout(DL);
+
+  IntTy = Type::getInt64Ty(M.getContext());
+  for (GlobalVariable &GV : M.globals())
+    if (GV.hasInitializer())
+      removeAddrSpaceCast(GV.getInitializer());
+  return true;
+}
diff --git a/unittests/BackendCore/BackendCABIOracleTests.cpp b/unittests/BackendCore/BackendCABIOracleTests.cpp
index 29ff26b..99f8108 100644
--- a/unittests/BackendCore/BackendCABIOracleTests.cpp
+++ b/unittests/BackendCore/BackendCABIOracleTests.cpp
@@ -20,7 +20,7 @@
 
 TEST(BackendCABIOracleTests, Basic) {
   LLVMContext C;
-  std::unique_ptr<Llvm_backend> bep(new Llvm_backend(C, nullptr, nullptr));
+  std::unique_ptr<Llvm_backend> bep(new Llvm_backend(C, nullptr, nullptr, 0));
   Llvm_backend *be = bep.get();
 
   Btype *bi8t = be->integer_type(false, 8);
@@ -58,7 +58,7 @@
 
 TEST(BackendCABIOracleTests, Extended) {
   LLVMContext C;
-  std::unique_ptr<Llvm_backend> bep(new Llvm_backend(C, nullptr, nullptr));
+  std::unique_ptr<Llvm_backend> bep(new Llvm_backend(C, nullptr, nullptr, 0));
   Llvm_backend *be = bep.get();
 
   Btype *bi8t = be->integer_type(false, 8);
diff --git a/unittests/BackendCore/BackendTreeIntegrity.cpp b/unittests/BackendCore/BackendTreeIntegrity.cpp
index c82ef1f..5beec52 100644
--- a/unittests/BackendCore/BackendTreeIntegrity.cpp
+++ b/unittests/BackendCore/BackendTreeIntegrity.cpp
@@ -64,7 +64,7 @@
 
   // Add the same Expression to more than one statement
   LLVMContext C;
-  std::unique_ptr<Llvm_backend> be(new Llvm_backend(C, nullptr, nullptr));
+  std::unique_ptr<Llvm_backend> be(new Llvm_backend(C, nullptr, nullptr, 0));
   be->disableIntegrityChecks();
 
   Location loc;
@@ -101,7 +101,7 @@
 
   // Same statement with more than one parent.
   LLVMContext C;
-  std::unique_ptr<Llvm_backend> be(new Llvm_backend(C, nullptr, nullptr));
+  std::unique_ptr<Llvm_backend> be(new Llvm_backend(C, nullptr, nullptr, 0));
   be->disableIntegrityChecks();
   Location loc;
   Bfunction *func = mkFunci32o64(be.get(), "foo");
diff --git a/unittests/BackendCore/TestUtils.cpp b/unittests/BackendCore/TestUtils.cpp
index 9bff12a..4f2c41e 100644
--- a/unittests/BackendCore/TestUtils.cpp
+++ b/unittests/BackendCore/TestUtils.cpp
@@ -374,7 +374,7 @@
 
 FcnTestHarness::FcnTestHarness(const char *fcnName)
     : context_()
-    , be_(new Llvm_backend(context_, nullptr, nullptr))
+    , be_(new Llvm_backend(context_, nullptr, nullptr, 0))
     , func_(nullptr)
     , entryBlock_(nullptr)
     , curBlock_(nullptr)