gollvm: update to LLVM mainline

Patch switches to mainline LLVM which implies following changes in
gollvm:

- Use of new pass manager
- Use of opaque pointers and implementing type tracking based on Btype
  instead of llvm::Type
- Use of debug records instead of intrinsics
- Fix gollvm unit tests

Change-Id: I3b8e3fe4820158a06fd1754ba2e1e0515d65ecca
Reviewed-on: https://go-review.googlesource.com/c/gollvm/+/642655
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Anton Korobeynikov <anton@korobeynikov.info>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2561f8b..f5b8baa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -114,6 +114,16 @@
   set(gocdep llvm-goc-token)
 endif()
 
+if(CXX_SUPPORTS_SUGGEST_OVERRIDE_FLAG)
+  add_definitions("-Wno-suggest-override")
+  set_target_properties(gollvm PROPERTIES INTERFACE_COMPILE_OPTIONS "-Wno-suggest-override")
+endif()
+
+if(CXX_SUPPORTS_COVERED_SWITCH_DEFAULT_FLAG)
+  add_definitions("-Wno-covered-switch-default")
+  set_target_properties(gollvm PROPERTIES INTERFACE_COMPILE_OPTIONS "-Wno-covered-switch-default")
+endif()
+
 # Root of gollvm source code.
 set(GOLLVM_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 
diff --git a/bridge/go-llvm-bexpression.cpp b/bridge/go-llvm-bexpression.cpp
index 1b97168..1b0ff3a 100644
--- a/bridge/go-llvm-bexpression.cpp
+++ b/bridge/go-llvm-bexpression.cpp
@@ -118,12 +118,6 @@
   if (!llvm::isa<llvm::Constant>(value_))
     return false;
 
-  // Not a constant if there is a pending load
-  if (value_->getType()->isPointerTy() &&
-      value_->getType()->getPointerElementType() ==
-        btype()->type())
-    return false;
-
   // In some cases, even the underlying value is an
   // llvm::Constant, the expression may be not. For
   // example, a var expression for a global variable,
diff --git a/bridge/go-llvm-bexpression.h b/bridge/go-llvm-bexpression.h
index 767ac47..9f4be88 100644
--- a/bridge/go-llvm-bexpression.h
+++ b/bridge/go-llvm-bexpression.h
@@ -83,10 +83,6 @@
   VarContext() : addrLevel_(0), lvalue_(false), pending_(false) { }
   VarContext(bool lvalue, unsigned addrLevel)
       : addrLevel_(addrLevel), lvalue_(lvalue), pending_(true) { }
-  explicit VarContext(const VarContext &src)
-      : addrLevel_(src.addrLevel_), lvalue_(src.lvalue_),
-        pending_(src.pending_)
-  { }
 
   bool pending() const { return pending_; }
   unsigned addrLevel() const { return addrLevel_; }
diff --git a/bridge/go-llvm-bfunction.cpp b/bridge/go-llvm-bfunction.cpp
index 20aed84..e4b741f 100644
--- a/bridge/go-llvm-bfunction.cpp
+++ b/bridge/go-llvm-bfunction.cpp
@@ -74,10 +74,9 @@
   return abiOracle_->tm()->tnamegen(tag);
 }
 
-llvm::Instruction *Bfunction::addAlloca(llvm::Type *typ,
-                                        const std::string &name)
-{
+llvm::Instruction *Bfunction::addAlloca(Btype *bty, const std::string &name) {
   llvm::Instruction *insBefore = nullptr;
+  llvm::Type *typ = bty->type();
   TypeManager *tm = abiOracle_->tm();
   llvm::Align aaAlign = tm->datalayout()->getABITypeAlign(typ);
   llvm::Value *aaSize = nullptr;
@@ -131,8 +130,7 @@
         // Seems weird to create a zero-sized alloca(), but it should
         // simplify things in that we can avoid having a Bvariable with a
         // null value.
-        llvm::Type *llpt = paramTypes[idx]->type();
-        llvm::Instruction *inst = addAlloca(llpt, "");
+        llvm::Instruction *inst = addAlloca(paramTypes[idx], "");
         paramValues_.push_back(inst);
         break;
       }
@@ -148,8 +146,7 @@
         break;
       }
       case ParmDirect: {
-        llvm::Type *llpt = paramTypes[idx]->type();
-        llvm::Instruction *inst = addAlloca(llpt, "");
+        llvm::Instruction *inst = addAlloca(paramTypes[idx], "");
         paramValues_.push_back(inst);
         if (paramInfo.attr() == AttrSext)
           arguments_[argIdx]->addAttr(llvm::Attribute::SExt);
@@ -229,7 +226,7 @@
   // Create the spill slot for the param.
   std::string spname(name);
   spname += ".addr";
-  llvm::Instruction *inst = addAlloca(btype->type(), spname);
+  llvm::Instruction *inst = addAlloca(btype, spname);
   assert(chainVal_);
   assert(llvm::isa<llvm::Argument>(chainVal_));
   chainVal_ = inst;
@@ -260,7 +257,7 @@
     // to share the same alloca instruction.
     inst = llvm::cast<llvm::Instruction>(declVar->value());
   } else {
-    inst = addAlloca(btype->type(), name);
+    inst = addAlloca(btype, name);
   }
   if (is_address_taken) {
     llvm::Instruction *alloca = inst;
@@ -285,12 +282,7 @@
 
 llvm::Value *Bfunction::createTemporary(Btype *btype, const std::string &tag)
 {
-  return createTemporary(btype->type(), tag);
-}
-
-llvm::Value *Bfunction::createTemporary(llvm::Type *typ, const std::string &tag)
-{
-  return addAlloca(typ, tag);
+  return addAlloca(btype, tag);
 }
 
 // This implementation uses an alloca instruction as a placeholder
@@ -378,13 +370,6 @@
     llvm::Argument *arg = arguments_[paramInfo.sigOffset()];
     assert(sploc->getType()->isPointerTy());
     llvm::PointerType *llpt = llvm::cast<llvm::PointerType>(sploc->getType());
-    if (paramInfo.abiType()->isVectorTy() ||
-        arg->getType() != llpt->getElementType()) {
-      std::string tag(namegen("cast"));
-      llvm::Type *ptv = llvm::PointerType::get(paramInfo.abiType(), 0);
-      llvm::Value *bitcast = builder.CreateBitCast(sploc, ptv, tag);
-      sploc = bitcast;
-    }
     llvm::Instruction *si = builder.CreateStore(arg, sploc);
     paramVar->setInitializer(si);
     spillInstructions->appendInstructions(builder.instructions());
@@ -398,16 +383,13 @@
   llvm::Type *llst = paramInfo.computeABIStructType(tm);
   llvm::Type *ptst = llvm::PointerType::get(llst, 0);
 
-  // Cast the spill location to a pointer to the struct created above.
-  std::string tag(namegen("cast"));
-  llvm::Value *bitcast = builder.CreateBitCast(sploc, ptst, tag);
   llvm::Instruction *stinst = nullptr;
 
   // Generate a store to each field.
   for (unsigned i = 0; i < paramInfo.abiTypes().size(); ++i) {
     std::string tag(namegen("field" + std::to_string(i)));
     llvm::Value *fieldgep =
-        builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, i, tag);
+        builder.CreateConstInBoundsGEP2_32(llst, sploc, 0, i, tag);
     llvm::Value *argChunk = arguments_[paramInfo.sigOffset() + i];
     stinst = builder.CreateStore(argChunk, fieldgep);
   }
@@ -457,11 +439,11 @@
   // Append allocas for local variables
   // FIXME: create lifetime annotations
   for (auto aa : allocas_)
-    entry->getInstList().push_back(aa);
+    aa->insertBefore(*entry, entry->end());
 
   // Param spills
   for (auto sp : spills.instructions())
-    entry->getInstList().push_back(sp);
+    sp->insertBefore(*entry, entry->end());
 
   // Debug meta-data generation needs to know the position at which a
   // parameter variable is available for inspection -- this is
@@ -496,11 +478,11 @@
   // we insert any new temps into the start of the block.
   if (! temps.empty())
     for (auto ai : temps) {
-      entry->getInstList().push_front(ai);
+      ai->insertInto(entry, entry->begin());
       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::cast<llvm::Instruction>(op)->insertInto(entry, entry->begin());
       }
     }
 }
@@ -547,10 +529,8 @@
                       returnInfo.abiType());
   llvm::Type *ptst = llvm::PointerType::get(llrt, 0);
   BlockLIRBuilder builder(function(), inamegen);
-  std::string castname(namegen("cast"));
-  llvm::Value *bitcast = builder.CreateBitCast(toRet->value(), ptst, castname);
   std::string loadname(namegen("ld"));
-  llvm::Instruction *ldinst = builder.CreateLoad(llrt, bitcast, loadname);
+  llvm::Instruction *ldinst = builder.CreateLoad(llrt, toRet->value(), loadname);
   retInstrs->appendInstructions(builder.instructions());
   return ldinst;
 }
diff --git a/bridge/go-llvm-bfunction.h b/bridge/go-llvm-bfunction.h
index f83aafa..e51cb91 100644
--- a/bridge/go-llvm-bfunction.h
+++ b/bridge/go-llvm-bfunction.h
@@ -124,7 +124,6 @@
 
   // Return an alloca temporary of the specified type.
   llvm::Value *createTemporary(Btype *btype, const std::string &tag);
-  llvm::Value *createTemporary(llvm::Type *type, const std::string &tag);
 
   // If the function return value is passing via memory instead of
   // directly, this function returns the location into which the
@@ -155,7 +154,7 @@
 
   // Create an alloca with the specified type. The alloca is recorded
   // in a list so that it can be picked up during prolog generation.
-  llvm::Instruction *addAlloca(llvm::Type *vtyp, const std::string &name);
+  llvm::Instruction *addAlloca(Btype *typ, const std::string &name);
 
   // Given an LLVM value, return the Bvariable we created to wrap it (either
   // local var or parameter var).
diff --git a/bridge/go-llvm-builtins.cpp b/bridge/go-llvm-builtins.cpp
index 8e7224a..606d02d 100644
--- a/bridge/go-llvm-builtins.cpp
+++ b/bridge/go-llvm-builtins.cpp
@@ -535,14 +535,13 @@
   return builder->CreateOr(old, args[1]);
 }
 
-static llvm::Value *atomicCasMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
-                                   BlockLIRBuilder *builder,
-                                   Llvm_backend *be)
-{
+static llvm::Value *atomicCasMaker(llvm::SmallVectorImpl<llvm::Value *> &args,
+                                   BlockLIRBuilder *builder, Llvm_backend *be,
+                                   llvm::Type *elemTy) {
   assert(args.size() == 6);
   // GCC __atomic_compare_exchange_n takes a pointer to the old value.
   // We need to load it.
-  llvm::Value *old = builder->CreateLoad(args[1]->getType()->getPointerElementType(), args[1]);
+  llvm::Value *old = builder->CreateLoad(elemTy, args[1]);
   // FIXME: see atomicLoadMaker, but default to SequentiallyConsistent
   // for success order, Monotonic (i.e. relaxed) for failed order,
   // and false for weak.
@@ -566,6 +565,18 @@
   return builder->CreateZExt(ret, be->llvmInt8Type());
 }
 
+static llvm::Value *atomicCasMaker4(llvm::SmallVectorImpl<llvm::Value *> &args,
+                                    BlockLIRBuilder *builder,
+                                    Llvm_backend *be) {
+  return atomicCasMaker(args, builder, be, be->llvmInt32Type());
+}
+
+static llvm::Value *atomicCasMaker8(llvm::SmallVectorImpl<llvm::Value *> &args,
+                                    BlockLIRBuilder *builder,
+                                    Llvm_backend *be) {
+  return atomicCasMaker(args, builder, be, be->llvmInt64Type());
+}
+
 void BuiltinTable::defineExprBuiltins()
 {
   Btype *boolType = tman_->boolType();
@@ -655,13 +666,13 @@
   {
     BuiltinEntryTypeVec typeVec = {boolType, uint32PtrType, uint32PtrType, uint32Type,
                                    boolType, int32Type, int32Type};
-    registerExprBuiltin("__atomic_compare_exchange_4", nullptr,
-                        typeVec, atomicCasMaker);
+    registerExprBuiltin("__atomic_compare_exchange_4", nullptr, typeVec,
+                        atomicCasMaker4);
   }
   {
     BuiltinEntryTypeVec typeVec = {boolType, uint64PtrType, uint64PtrType, uint64Type,
                                    boolType, int32Type, int32Type};
-    registerExprBuiltin("__atomic_compare_exchange_8", nullptr,
-                        typeVec, atomicCasMaker);
+    registerExprBuiltin("__atomic_compare_exchange_8", nullptr, typeVec,
+                        atomicCasMaker8);
   }
 }
diff --git a/bridge/go-llvm-dibuildhelper.cpp b/bridge/go-llvm-dibuildhelper.cpp
index 777b8d9..de94946 100644
--- a/bridge/go-llvm-dibuildhelper.cpp
+++ b/bridge/go-llvm-dibuildhelper.cpp
@@ -156,13 +156,7 @@
   if(var->initializer() && !var->initializerInstruction()->getParent())
     return;
 
-  // Create the declare instruction, giving it an initial position at
-  // the end of the entry block (the insertDeclare call below doesn't
-  // allow a NULL insert location, so we pick end-of-block arbitrarily).
   assert(entryBlock_);
-  llvm::Instruction *decl =
-      dibuilder().insertDeclare(var->value(), dilv, expr, vloc, entryBlock_);
-  decl->removeFromParent();
 
   // Extract the decl from the end of the entry block and reposition
   // it according to the var properties.
@@ -173,7 +167,11 @@
   } else if (var->flavor() == ParamVar) {
     // parameters passing by reference may have no initializers.
     // declare them at function entry.
-    entryBlock_->getInstList().push_front(decl);
+    if (!entryBlock_->empty())
+      dibuilder().insertDeclare(var->value(), dilv, expr, vloc,
+                                &entryBlock_->front());
+    else
+      dibuilder().insertDeclare(var->value(), dilv, expr, vloc, entryBlock_);
     return;
   } else {
     // locals with no initializer should only be zero-sized vars.
@@ -185,7 +183,12 @@
 
   assert(! llvm::isa<llvm::BranchInst>(insertionPoint));
   assert(insertionPoint != insertionPoint->getParent()->getTerminator());
-  decl->insertAfter(insertionPoint);
+  if (insertionPoint->getNextNode())
+    dibuilder().insertDeclare(var->value(), dilv, expr, vloc,
+                              insertionPoint->getNextNode());
+  else
+    dibuilder().insertDeclare(var->value(), dilv, expr, vloc,
+                              insertionPoint->getParent());
 }
 
 void DIBuildHelper::endFunction(Bfunction *function)
@@ -310,7 +313,7 @@
 
 std::string DIBuildHelper::applyDebugPrefix(llvm::StringRef path) {
   for (const auto &remap : debugPrefixMap_)
-    if (path.startswith(remap.first))
+    if (path.starts_with(remap.first))
       return (llvm::Twine(remap.second) +
               path.substr(remap.first.size())).str();
   return path.str();
diff --git a/bridge/go-llvm-irbuilders.cpp b/bridge/go-llvm-irbuilders.cpp
index de007b1..d883cec 100644
--- a/bridge/go-llvm-irbuilders.cpp
+++ b/bridge/go-llvm-irbuilders.cpp
@@ -25,14 +25,14 @@
 
 BlockLIRBuilder::~BlockLIRBuilder()
 {
-  assert(dummyBlock_->getInstList().empty());
+  assert(dummyBlock_->empty());
   dummyBlock_->removeFromParent();
 }
 
 std::vector<llvm::Instruction*> BlockLIRBuilder::instructions()
 {
   std::vector<llvm::Instruction*> rv;
-  for (auto &i : dummyBlock_->getInstList())
+  for (auto &i : *dummyBlock_)
     rv.push_back(&i);
   for (auto &i : rv) {
     i->removeFromParent();
diff --git a/bridge/go-llvm-irbuilders.h b/bridge/go-llvm-irbuilders.h
index 537eb1d..d54f1b6 100644
--- a/bridge/go-llvm-irbuilders.h
+++ b/bridge/go-llvm-irbuilders.h
@@ -31,8 +31,7 @@
   void setDest(Bexpression *expr) { assert(!expr_); expr_ = expr; }
 
   void InsertHelper(llvm::Instruction *I, const llvm::Twine &Name,
-                    llvm::BasicBlock *BB,
-                    llvm::BasicBlock::iterator InsertPt) const {
+                    llvm::BasicBlock::iterator InsertPt) const override {
     assert(expr_);
     expr_->appendInstruction(I);
     I->setName(Name);
diff --git a/bridge/go-llvm-materialize.cpp b/bridge/go-llvm-materialize.cpp
index 8e89288..3d2bc1a 100644
--- a/bridge/go-llvm-materialize.cpp
+++ b/bridge/go-llvm-materialize.cpp
@@ -121,8 +121,7 @@
   assert(valtyp->isPointerTy());
   if (valtyp->getPointerAddressSpace() != addressSpace_) {
     llvm::Type *typ =
-        llvm::PointerType::get(valtyp->getPointerElementType(),
-                               addressSpace_);
+        llvm::PointerType::get(valtyp->getContext(), addressSpace_);
     val = new llvm::AddrSpaceCastInst(val, typ, "ascast");
   }
 
@@ -176,9 +175,8 @@
     }
     if (lvalue || useCopyForLoadStore(type->type())) {
       llvm::Type *et = expr->btype()->type();
-      if (valType->isPointerTy() &&
-          valType->getPointerElementType() == et)
-        toType = llvm::PointerType::get(toType, addressSpace_);
+      if (valType->isPointerTy())
+        toType = llvm::PointerType::get(toType->getContext(), addressSpace_);
     }
   }
 
@@ -230,8 +228,7 @@
     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);
+      llvm::Type *pt = llvm::PointerType::get(val->getContext(), 0);
       std::string tname(namegen("ascast"));
       val = builder.CreateAddrSpaceCast(val, pt, tname);
       expr = nbuilder_.mkConversion(type, val, expr, location);
@@ -248,8 +245,7 @@
     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);
+      pt = llvm::PointerType::get(toType->getContext(), 0);
     }
     std::string tname(namegen("itpcast"));
     llvm::Value *itpcast = builder.CreateIntToPtr(val, pt, tname);
@@ -342,14 +338,13 @@
   return rval;
 }
 
-llvm::Value *Llvm_backend::makePointerOffsetGEP(llvm::PointerType *llpt,
-                                                llvm::Value *idxval,
-                                                llvm::Value *sptr)
-{
+llvm::Value *Llvm_backend::makePointerOffsetGEP(Btype *pt, llvm::Value *idxval,
+                                                llvm::Value *sptr) {
   LIRBuilder builder(context_, llvm::ConstantFolder());
   llvm::SmallVector<llvm::Value *, 1> elems(1);
   elems[0] = idxval;
-  llvm::Value *val = builder.CreateGEP(llpt->getElementType(), sptr, elems, namegen("ptroff"));
+  llvm::Type *eltTy = pt->castToBPointerType()->toType()->type();
+  llvm::Value *val = builder.CreateGEP(eltTy, sptr, elems, namegen("ptroff"));
   return val;
 }
 
@@ -365,12 +360,10 @@
   return val;
 }
 
-llvm::Value *Llvm_backend::makeFieldGEP(unsigned fieldIndex,
-                                        llvm::Value *sptr)
-{
+llvm::Value *Llvm_backend::makeFieldGEP(unsigned fieldIndex, llvm::Type *sty,
+                                        llvm::Value *sptr) {
   assert(sptr->getType()->isPointerTy());
-  llvm::PointerType *srcTyp = llvm::cast<llvm::PointerType>(sptr->getType());
-  llvm::StructType *llst = llvm::cast<llvm::StructType>(srcTyp->getElementType());
+  llvm::StructType *llst = llvm::cast<llvm::StructType>(sty);
   LIRBuilder builder(context_, llvm::ConstantFolder());
   assert(fieldIndex < llst->getNumElements());
   std::string tag(namegen("field"));
@@ -401,7 +394,7 @@
   if (bstruct->isConstant())
     fval = llvm::cast<llvm::Constant>(sval)->getAggregateElement(index);
   else
-    fval = makeFieldGEP(index, sval);
+    fval = makeFieldGEP(index, llt, sval);
   Btype *bft = elementTypeByIndex(bstruct->btype(), index);
 
   // Wrap result in a Bexpression
@@ -632,29 +625,9 @@
   if (leftType == rightType)
     return rval;
 
-  // Case 1: nil op X
-  if (llvm::isa<llvm::ConstantPointerNull>(leftVal) &&
-      rightType->isPointerTy()) {
-    BexprLIRBuilder builder(context_, left);
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder.CreateBitCast(leftVal, rightType, tag);
-    rval.first = bitcast;
-    return rval;
-  }
-
-  // Case 2: X op nil
-  if (llvm::isa<llvm::ConstantPointerNull>(rightVal) &&
-      leftType->isPointerTy()) {
-    BexprLIRBuilder builder(context_, right);
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder.CreateBitCast(rightVal, leftType, tag);
-    rval.second = bitcast;
-    return rval;
-  }
-
   // Case 3: shift with different sized operands (ex: int64(v) << uint8(3)).
   // Promote or demote shift amount operand to match width of left operand.
-  if ((op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT) &&
+  if ((op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT || op == OPERATOR_EQEQ) &&
       leftType != rightType) {
     BexprLIRBuilder builder(context_, right);
     llvm::IntegerType *leftITyp = llvm::cast<llvm::IntegerType>(leftType);
@@ -668,24 +641,6 @@
     return rval;
   }
 
-  // Case 4: pointer type comparison
-  // We check that if both are pointer types and pointing to the same type,
-  // insert a cast (it doesn't matter we cast which one). This mostly needed
-  // for circular pointer types (ex: type T *T; var p, q T; p == &q), where
-  // the two sides have semantically identical types but with different
-  // representations (in this case, T vs. *T).
-  if (leftType->isPointerTy() && rightType->isPointerTy()) {
-    BPointerType *lbpt = left->btype()->castToBPointerType();
-    BPointerType *rbpt = right->btype()->castToBPointerType();
-    if (lbpt->toType()->type() == rbpt->toType()->type()) {
-      BexprLIRBuilder builder(context_, right);
-      std::string tag(namegen("cast"));
-      llvm::Value *bitcast = builder.CreateBitCast(rightVal, leftType, tag);
-      rval.second = bitcast;
-      return rval;
-    }
-  }
-
   return rval;
 }
 
@@ -1024,10 +979,8 @@
   base = resolveVarContext(base);
 
   // Construct an appropriate GEP
-  llvm::PointerType *llpt =
-      llvm::cast<llvm::PointerType>(base->btype()->type());
   llvm::Value *gep =
-      makePointerOffsetGEP(llpt, index->value(), base->value());
+      makePointerOffsetGEP(base->btype(), index->value(), base->value());
 
   // Wrap in a Bexpression
   Bexpression *rval = nbuilder_.mkPointerOffset(base->btype(), gep, base,
@@ -1192,7 +1145,7 @@
       if (paramInfo.attr() == AttrByVal && vt->getPointerAddressSpace() != 0) {
         // We pass a stack address, which is always in address space 0.
         std::string castname(namegen("ascast"));
-        llvm::Type *pt = llvm::PointerType::get(vt->getPointerElementType(), 0);
+        llvm::Type *pt = llvm::PointerType::get(vt->getContext(), 0);
         val = builder.CreateAddrSpaceCast(val, pt, castname);
       }
 
@@ -1240,29 +1193,13 @@
           Bvariable *cv = genVarForConstant(cval, resarg->btype());
           val = cv->value();
         }
-        std::string castname(namegen("cast"));
-        // We are going to do a load, so the address space does not matter.
-        // It seems we may get here with either address space, so we just
-        // do an address-space-preserving cast.
-        llvm::Type *ptv =
-            llvm::PointerType::get(paramInfo.abiType(),
-                                   val->getType()->getPointerAddressSpace());
-        llvm::Value *bitcast = builder.CreateBitCast(val, ptv, castname);
         std::string ltag(namegen("ld"));
-        llvm::Value *ld = builder.CreateLoad(paramInfo.abiType(), bitcast, ltag);
+        llvm::Value *ld = builder.CreateLoad(paramInfo.abiType(), val, ltag);
         state.llargs.push_back(ld);
         continue;
       }
       // Passing a single 8-byte-or-less argument.
 
-      // Apply any necessary pointer type conversions.
-      if (val->getType()->isPointerTy() && ctx == VE_rvalue) {
-        llvm::FunctionType *llft =
-            llvm::cast<llvm::FunctionType>(state.calleeFcnType->type());
-        llvm::Type *paramTyp = llft->getParamType(paramInfo.sigOffset());
-        val = convertForAssignment(resarg, paramTyp);
-      }
-
       // Apply any necessary sign-extensions or zero-extensions.
       if (paramInfo.abiType()->isIntegerTy()) {
         if (paramInfo.attr() == AttrZext)
@@ -1283,7 +1220,6 @@
 
     // Create a struct type of the appropriate shape
     llvm::Type *llst = paramInfo.computeABIStructType(typeManager());
-    llvm::Type *ptst = makeLLVMPointerType(llst);
 
     // If the value we're passing is a composite constant, we have to
     // spill it to memory here in order for the casts below to work.
@@ -1299,16 +1235,11 @@
       val = cv->value();
     }
 
-    // Cast the value to the struct type
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast =
-        builder.CreatePointerBitCastOrAddrSpaceCast(val, ptst, tag);
-
     // Load up each field
     for ( unsigned i = 0; i < paramInfo.abiTypes().size(); ++i) {
       std::string ftag(namegen("field"+std::to_string(i)));
       llvm::Value *fieldgep =
-          builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, i, ftag);
+          builder.CreateConstInBoundsGEP2_32(llst, val, 0, i, ftag);
       std::string ltag(namegen("ld"));
       llvm::Value *ld = builder.CreateLoad(paramInfo.abiTypes()[i], fieldgep, ltag);
       state.llargs.push_back(ld);
@@ -1389,13 +1320,8 @@
       llvm::Type *rt = (returnInfo.abiTypes().size() == 1 ?
                         returnInfo.abiType()  :
                         returnInfo.computeABIStructType(typeManager()));
-      llvm::Type *ptrt = llvm::PointerType::get(rt, 0);
-      std::string castname(namegen("cast"));
-      llvm::Value *bitcast =
-          state.builder.CreateBitCast(state.sretTemp,
-                                      ptrt, castname);
       std::string stname(namegen("st"));
-      state.builder.CreateStore(callInst, bitcast);
+      state.builder.CreateStore(callInst, state.sretTemp);
       callExpr->appendInstructions(state.builder.instructions());
     }
   }
@@ -1605,152 +1531,6 @@
   return rval;
 }
 
-llvm::Value *
-Llvm_backend::convertForAssignment(Bexpression *src,
-                                   llvm::Type *dstToType)
-{
-  if (src->value()->getType() == dstToType)
-    return src->value();
-
-  llvm::Function *dummyFcn = errorFunction_->function();
-  BlockLIRBuilder builder(dummyFcn, this);
-  llvm::Value *val = convertForAssignment(src->btype(), src->value(),
-                                          dstToType, &builder);
-  src->appendInstructions(builder.instructions());
-  return val;
-}
-
-llvm::Value *
-Llvm_backend::convertForAssignment(Btype *srcBType,
-                                   llvm::Value *srcVal,
-                                   llvm::Type *dstToType,
-                                   BlockLIRBuilder *builder)
-{
-  llvm::Type *srcType = srcVal->getType();
-
-  if (dstToType == srcType)
-    return srcVal;
-
-  // Case 1: handle discrepancies between representations of function
-  // descriptors. All front end function descriptor types are structs
-  // with a single field, however this field can sometimes be a pointer
-  // to function, and sometimes it can be of uintptr type.
-  bool srcPtrToFD = isPtrToFuncDescriptorType(srcType);
-  bool dstPtrToFD = isPtrToFuncDescriptorType(dstToType);
-  if (srcPtrToFD && dstPtrToFD) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 2: handle circular function types.
-  bool dstCircFunc = isCircularFunctionType(dstToType);
-  bool srcCircFunc = isCircularFunctionType(srcType);
-  bool srcFuncPtr = isPtrToFuncType(srcType);
-  if (((srcPtrToFD || srcFuncPtr || srcCircFunc) && dstCircFunc) ||
-       (srcCircFunc && dstPtrToFD)) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 3: handle raw function pointer assignment (frontend will
-  // sometimes take a function pointer and assign it to "void *" without
-  // an explicit conversion).
-  bool dstPtrToVoid = isPtrToVoidType(dstToType);
-  if (dstPtrToVoid && srcFuncPtr) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 4: in some cases when calling a function (for example, __gogo)
-  // the front end will pass a raw function vale in place of a function
-  // descriptor. Allow this case.
-  if (dstPtrToFD && srcFuncPtr) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast =
-        builder->CreatePointerBitCastOrAddrSpaceCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 5: handle polymorphic nil pointer expressions-- these are
-  // generated without a type initially, so we need to convert them
-  // to the appropriate type if they appear in an assignment context.
-  if (srcVal == nil_pointer_expression()->value()) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 6: the code that creates interface values stores pointers of
-  // various flavors into i8*. Ideally it would be better to insert
-  // forced type conversions in the FE, but for now we allow this case.
-  bool srcPtrToIface = isPtrToIfaceStructType(srcType);
-  if (dstPtrToVoid && srcPtrToIface) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 7: when creating slice values it's common for the frontend
-  // to mix pointers and arrays, e.g. assign "[3 x i64]*" to "i64**".
-  // Allow this sort of conversion.
-  llvm::Type *elt =
-      (dstToType->isPointerTy() ?
-       llvm::cast<llvm::PointerType>(dstToType)->getElementType() : nullptr);
-  if (elt && isPtrToArrayOf(srcType, elt)) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 8: also when creating slice values it's common for the
-  // frontend to assign pointer-to-X to unsafe.Pointer (and vice versa)
-  // without an explicit cast. Allow this for now.
-  if ((dstToType == llvmPtrType() && llvm::isa<llvm::PointerType>(srcType)) ||
-      (srcType == llvmPtrType() && llvm::isa<llvm::PointerType>(dstToType))) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast = builder->CreatePointerBitCastOrAddrSpaceCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 9: related to case 1 above, interface value expressions can
-  // contain C function pointers stored as "void *" instead of
-  // concrete pointer-to-function values. For example, consider a
-  // value V1 of the form
-  //
-  //       { { T1*, void* }, O* }
-  //
-  // where T1 is a type descriptor and O is an object; this value can
-  // sometimes be assigned to a location of type
-  //
-  //       { { T1*, F* }, O* }
-  //
-  // where F is a concrete C pointer-to-function (as opposed to "void*").
-  // Allow conversions of this sort for now.
-  std::set<llvm::Type *> visited;
-  if (fcnPointerCompatible(dstToType, srcType, visited)) {
-    std::string tag(namegen("cast"));
-    llvm::Value *bitcast =
-        builder->CreatePointerBitCastOrAddrSpaceCast(srcVal, dstToType, tag);
-    return bitcast;
-  }
-
-  // Case 10: circular pointer type (ex: type T *T; var p T; p = &p)
-  BPointerType *srcbpt = srcBType->castToBPointerType();
-  if (srcbpt) {
-    Btype *ctypconv = circularTypeAddrConversion(srcbpt->toType());
-    if (ctypconv != nullptr && ctypconv->type() == dstToType) {
-      std::string tag(namegen("cast"));
-      llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
-      return bitcast;
-    }
-  }
-
-  return srcVal;
-}
-
 // Walk the specified expression and invoke setVarExprPending on
 // each var expression, with correct lvalue/rvalue tag depending on
 // context.
diff --git a/bridge/go-llvm-typemanager.cpp b/bridge/go-llvm-typemanager.cpp
index 4301233..d853e43 100644
--- a/bridge/go-llvm-typemanager.cpp
+++ b/bridge/go-llvm-typemanager.cpp
@@ -1008,8 +1008,8 @@
   // Circular function/pointer types have isPlaceholder false, but they do
   // need to resolve, so special-case them.
   if (!placeholder->isPlaceholder() &&
-      circularPointerTypes_.find(placeholder->type()) == circularPointerTypes_.end() &&
-      circularFunctionTypes_.find(placeholder->type()) == circularFunctionTypes_.end())
+      circularPointerTypes_.find(placeholder) == circularPointerTypes_.end() &&
+      circularFunctionTypes_.find(placeholder) == circularFunctionTypes_.end())
     return true;
 
   assert(anonTypes_.find(placeholder) == anonTypes_.end());
@@ -1038,8 +1038,8 @@
       }
 
       // Circular pointer type handling
-      circularConversionLoadMap_[cpt->type()] = elt;
-      circularConversionAddrMap_[elt->type()] = cpt;
+      circularConversionLoadMap_[cpt] = elt;
+      circularConversionAddrMap_[elt] = cpt;
 
       return setPlaceholderPointerType(placeholder, cpt);
     }
@@ -1056,9 +1056,9 @@
     std::cerr << "redir: "; to_type->dump();
   }
 
-  auto it = circularFunctionTypes_.find(placeholder->type());
+  auto it = circularFunctionTypes_.find(placeholder);
   if (it != circularFunctionTypes_.end())
-    circularFunctionTypes_.insert(to_type->type());
+    circularFunctionTypes_.insert(to_type);
 
   // Update the target type for the pointer
   BPointerType *bpt = placeholder->castToBPointerType();
@@ -1251,11 +1251,11 @@
     // Push marker and placeholder onto a stack so that we can
     // update them when the appropriate function type is created.
     circularFunctionPlaceholderTypes_.insert(placeholder);
-    circularFunctionTypes_.insert(rval->type());
+    circularFunctionTypes_.insert(rval);
   } else {
     // Set up to start tracking the types that will make up the
     // loop involved in the cycle.
-    circularPointerTypes_.insert(circ_typ);
+    circularPointerTypes_.insert(rval);
   }
 
   if (traceLevel() > 1) {
@@ -1278,35 +1278,25 @@
   auto it = circularFunctionPlaceholderTypes_.find(btype);
   if (it != circularFunctionPlaceholderTypes_.end())
     return true;
-  return isCircularFunctionType(btype->type());
-}
-
-bool TypeManager::isCircularFunctionType(llvm::Type *typ) {
-  assert(typ);
-  auto it = circularFunctionTypes_.find(typ);
-  return it != circularFunctionTypes_.end();
+  auto it2 = circularFunctionTypes_.find(btype);
+  return it2 != circularFunctionTypes_.end();
 }
 
 // Return whether we might be looking at a circular pointer type.
 
-bool TypeManager::isCircularPointerType(Btype *btype) {
-  assert(btype);
-  return isCircularPointerType(btype->type());
-}
-
-bool TypeManager::isCircularPointerType(llvm::Type *typ) {
+bool TypeManager::isCircularPointerType(Btype *typ) {
   assert(typ);
   auto it = circularPointerTypes_.find(typ);
   return it != circularPointerTypes_.end();
 }
 
 Btype *TypeManager::circularTypeLoadConversion(Btype *typ) {
-  auto it = circularConversionLoadMap_.find(typ->type());
+  auto it = circularConversionLoadMap_.find(typ);
   return it != circularConversionLoadMap_.end()  ? it->second : nullptr;
 }
 
 Btype *TypeManager::circularTypeAddrConversion(Btype *typ) {
-  auto it = circularConversionAddrMap_.find(typ->type());
+  auto it = circularConversionAddrMap_.find(typ);
   if (it != circularConversionAddrMap_.end())
     return it->second;
   return nullptr;
@@ -1506,7 +1496,7 @@
   if (btype == errorType_)
     return 1;
   llvm::Type *toget = getPlaceholderProxyIfNeeded(btype);
-  unsigned uval = datalayout_->getABITypeAlignment(toget);
+  unsigned uval = datalayout_->getABITypeAlign(toget).value();
   return static_cast<int64_t>(uval);
 }
 
@@ -1538,7 +1528,7 @@
   llvm::StructType *dummyst = llvm::StructType::get(context_, elems);
   const llvm::StructLayout *sl = datalayout_->getStructLayout(dummyst);
   uint64_t uoff = sl->getElementOffset(1);
-  unsigned talign = datalayout_->getABITypeAlignment(toget);
+  unsigned talign = datalayout_->getABITypeAlign(toget).value();
   int64_t rval = (uoff < talign ? uoff : talign);
   return rval;
 }
@@ -1564,134 +1554,6 @@
   return static_cast<int64_t>(uoff);
 }
 
-bool TypeManager::isPtrToIfaceStructType(llvm::Type *typ)
-{
-  if (! typ->isPointerTy())
-    return false;
-  llvm::PointerType *pt = llvm::cast<llvm::PointerType>(typ);
-  llvm::Type *elt = pt->getElementType();
-  if (! elt->isStructTy())
-    return false;
-  llvm::StructType *st = llvm::cast<llvm::StructType>(elt);
-  if (st->getNumElements() != 2)
-    return false;
-  llvm::SmallPtrSet<llvm::Type *, 32> vis;
-  // expect { ptr, ptr } or { ptr, uintptr }
-  return (st->getElementType(0)->isPointerTy() &&
-          (st->getElementType(1)->isPointerTy() ||
-           st->getElementType(1) == llvmIntegerType()));
-}
-
-bool TypeManager::isFuncDescriptorType(llvm::Type *typ)
-{
-  if (! typ->isStructTy())
-    return false;
-  llvm::StructType *st = llvm::cast<llvm::StructType>(typ);
-  if (st->getNumElements() != 1)
-    return false;
-  llvm::Type *f0t = st->getElementType(0);
-  llvm::PointerType *f0tpt = nullptr;
-  if (f0t->isPointerTy())
-    f0tpt = llvm::cast<llvm::PointerType>(f0t);
-  if (f0t != llvmIntegerType_ &&
-      !f0t->isFunctionTy() &&
-      !(f0tpt && f0tpt->getElementType()->isFunctionTy()))
-    return false;
-  return true;
-}
-
-bool TypeManager::isPtrToFuncDescriptorType(llvm::Type *typ)
-{
-  if (! typ->isPointerTy())
-    return false;
-  llvm::PointerType *pt = llvm::cast<llvm::PointerType>(typ);
-  return isFuncDescriptorType(pt->getElementType());
-}
-
-bool TypeManager::isPtrToFuncType(llvm::Type *typ)
-{
-  if (! typ->isPointerTy())
-    return false;
-  llvm::PointerType *pt = llvm::cast<llvm::PointerType>(typ);
-  return pt->getElementType()->isFunctionTy();
-}
-
-bool TypeManager::isPtrToVoidType(llvm::Type *typ)
-{
-  if (! typ->isPointerTy())
-    return false;
-  llvm::PointerType *pt = llvm::cast<llvm::PointerType>(typ);
-  return pt->getElementType() == llvmInt8Type_;
-}
-
-bool TypeManager::isPtrToArrayOf(llvm::Type *typ, llvm::Type *arElmTyp)
-{
-  if (! typ->isPointerTy())
-    return false;
-  llvm::PointerType *pt = llvm::cast<llvm::PointerType>(typ);
-  llvm::Type *elt = pt->getElementType();
-  if (! elt->isArrayTy())
-    return false;
-  llvm::ArrayType *llat = llvm::cast<llvm::ArrayType>(elt);
-  llvm::Type *aelt = llat->getElementType();
-  if (aelt == arElmTyp)
-    return true;
-  if (isCircularFunctionType(aelt) && isCircularFunctionType(arElmTyp))
-    return true; // TODO: check they are same circular function type?
-  return false;
-}
-
-bool TypeManager::fcnPointerCompatible(llvm::Type *left,
-                                       llvm::Type *right,
-                                       std::set<llvm::Type *> &visited)
-{
-  // Allow for pointer-to-fp and func-desc matching
-  bool leftFPD = isPtrToFuncType(left) || isPtrToVoidType(left);
-  bool rightFPD = isPtrToFuncType(right) || isPtrToVoidType(right);
-  if (leftFPD && rightFPD)
-    return true;
-
-  bool visleft = (visited.find(left) != visited.end());
-  bool visright = (visited.find(right) != visited.end());
-  if (visleft != visright)
-    return false;
-  if (visleft)
-    return true;
-  visited.insert(left);
-  visited.insert(right);
-
-  // Compare type ID, children, etc.
-  if (left->getTypeID() != right->getTypeID())
-    return false;
-
-  // For pointer types, visit pointed-to elements
-  if (left->isPointerTy()) {
-    llvm::PointerType *ptl = llvm::cast<llvm::PointerType>(left);
-    llvm::PointerType *ptr = llvm::cast<llvm::PointerType>(right);
-    llvm::Type *eltl = ptl->getElementType();
-    llvm::Type *eltr = ptr->getElementType();
-    return fcnPointerCompatible(eltl, eltr, visited);
-  }
-
-  // For aggregate types, compare children.
-  if (left->isAggregateType()) {
-    unsigned leftnct = left->getNumContainedTypes();
-    unsigned rightnct = right->getNumContainedTypes();
-    if (leftnct != rightnct)
-      return false;
-    for (unsigned cti = 0; cti < leftnct; cti++) {
-      llvm::Type *leftchild = left->getContainedType(cti);
-      llvm::Type *rightchild = right->getContainedType(cti);
-      if (!fcnPointerCompatible(leftchild, rightchild, visited))
-        return false;
-    }
-    return true;
-  } else {
-    // For non-aggregate types, we expect underlying llvm types to match
-    return (left == right);
-  }
-}
-
 std::string TypeManager::typToString(Btype *typ)
 {
   std::map<Btype *, std::string> smap;
@@ -1750,7 +1612,7 @@
       assert(!bpt->isPlaceholder());
 
       // handle circular pointer types
-      auto cpit = circularPointerTypes_.find(typ->type());
+      auto cpit = circularPointerTypes_.find(typ);
       if (cpit != circularPointerTypes_.end()) {
         std::string s;
         llvm::raw_string_ostream os(s);
@@ -1848,7 +1710,7 @@
   // Now create struct type itself.  Q: should this be
   // getTypeAllocSize here instead of getTypeSizeInBits?
   uint64_t sizeInBits = datalayout_->getTypeSizeInBits(bst->type());
-  uint32_t alignInBits = datalayout_->getABITypeAlignment(bst->type());
+  uint32_t alignInBits = datalayout_->getABITypeAlign(bst->type()).value();
   llvm::DIType *derivedFrom = nullptr;
   llvm::DICompositeType *dist =
       dibuilder.createStructType(scope, typToString(bst),
@@ -1976,7 +1838,7 @@
       uint64_t arElems = bat->nelSize();
       uint64_t arSize = datalayout_->getTypeSizeInBits(bat->type());
       uint64_t arAlign =
-          datalayout_->getABITypeAlignment(bat->elemType()->type());
+          datalayout_->getABITypeAlign(bat->elemType()->type()).value();
       llvm::SmallVector<llvm::Metadata *, 1> subscripts;
       subscripts.push_back(dibuilder.getOrCreateSubrange(0, arElems));
       llvm::DINodeArray subsAr = dibuilder.getOrCreateArray(subscripts);
diff --git a/bridge/go-llvm-typemanager.h b/bridge/go-llvm-typemanager.h
index d77a3ab..4db9ccc 100644
--- a/bridge/go-llvm-typemanager.h
+++ b/bridge/go-llvm-typemanager.h
@@ -74,9 +74,7 @@
   Btype *namedType(const std::string &, Btype *, Location);
   Btype *circularPointerType(Btype *, bool);
   bool isCircularPointerType(Btype *);
-  bool isCircularPointerType(llvm::Type *);
   bool isCircularFunctionType(Btype *);
-  bool isCircularFunctionType(llvm::Type *);
   int64_t typeSize(Btype *);
   int64_t typeAlignment(Btype *);
   int64_t typeFieldAlignment(Btype *);
@@ -205,24 +203,6 @@
   // found.
   bool addPlaceholderRefs(Btype *type);
 
-  // Helpers
-  bool isFuncDescriptorType(llvm::Type *typ);
-  bool isPtrToFuncDescriptorType(llvm::Type *typ);
-  bool isPtrToIfaceStructType(llvm::Type *typ);
-  bool isPtrToFuncType(llvm::Type *typ);
-  bool isPtrToVoidType(llvm::Type *typ);
-  bool isPtrToArrayOf(llvm::Type *ptyp, llvm::Type *arrayElmTyp);
-
-  // This helper looks at two LLVM types and does a structural
-  // comparison to determine if 'left' is equivalent to 'right' modulo
-  // discrepancies between raw function pointers and "void *" (or
-  // equivalent). This is to allow for cases where the front end will
-  // store a function pointer in a table or struct somewhere as "void
-  // *" instead of the precise function type.
-  bool fcnPointerCompatible(llvm::Type *left,
-                            llvm::Type *right,
-                            std::set<llvm::Type *> &visited);
-
   // If specified type is a pointer flagged as being a circular
   // type, return conversion needed on load from that type, or NULL
   // if the type is not circular.
@@ -337,15 +317,15 @@
 
   // Set of circular pointer types. These are pointers to opaque types that
   // are returned by the ::circular_pointer_type() method.
-  std::unordered_set<llvm::Type *> circularPointerTypes_;
+  std::unordered_set<Btype *> circularPointerTypes_;
 
   // Map from placeholder type to circular pointer type. Key is placeholder
   // pointer type, value is circular pointer type marker.
   std::unordered_map<Btype *, Btype *> circularPointerTypeMap_;
 
   // Maps for inserting conversions involving circular pointers.
-  std::unordered_map<llvm::Type *, Btype *> circularConversionLoadMap_;
-  std::unordered_map<llvm::Type *, Btype *> circularConversionAddrMap_;
+  std::unordered_map<Btype *, Btype *> circularConversionLoadMap_;
+  std::unordered_map<Btype *, Btype *> circularConversionAddrMap_;
 
   // Set of top-level circular function types.
   std::unordered_set<Btype *> circularFunctionPlaceholderTypes_;
@@ -353,7 +333,7 @@
   // This set holds the marker types returned for the self-referential
   // elements within a circular function type, also any resolved LLVM
   // function types created from placeholders.
-  std::unordered_set<llvm::Type *> circularFunctionTypes_;
+  std::unordered_set<Btype *> circularFunctionTypes_;
 
   // Name generation helper
   NameGen *nametags_;
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 8ee5a24..be0f0bc 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -412,10 +412,7 @@
 Bfunction *Llvm_backend::createIntrinsicFcn(const std::string &name,
                                             llvm::Function *fcn)
 {
-  llvm::PointerType *llpft =
-      llvm::cast<llvm::PointerType>(fcn->getType());
-  llvm::FunctionType *llft =
-      llvm::cast<llvm::FunctionType>(llpft->getElementType());
+  llvm::FunctionType *llft = fcn->getFunctionType();
   BFunctionType *fcnType = makeAuxFcnType(llft);
   Location pdcl = linemap()->get_predeclared_location();
   Bfunction *bfunc = new Bfunction(fcn, fcnType, name, name, pdcl,
@@ -567,18 +564,10 @@
   llvm::Type *vt = val->getType();
   assert(vt->isPointerTy());
   llvm::Type *llToType = toType->type();
-  if (expr->varExprPending()) {
-    llvm::Type *et = expr->btype()->type();
-    if (vt->getPointerElementType() == et)
-      llToType = llvm::PointerType::get(llToType, vt->getPointerAddressSpace());
-  }
   if (vt == llToType)
     return expr;
 
-  std::string tag(namegen("cast"));
-  LIRBuilder builder(context_, llvm::ConstantFolder());
-  llvm::Value *bitcast = builder.CreatePointerBitCastOrAddrSpaceCast(val, llToType, tag);
-  return nbuilder_.mkConversion(toType, bitcast, expr, loc);
+  return nbuilder_.mkConversion(toType, val, expr, loc);
 }
 
 Bexpression *Llvm_backend::genLoad(Bexpression *expr,
@@ -593,6 +582,7 @@
   // pointer types.
   Bexpression *space = expr;
   Btype *loadResultType;
+  bool needs_extra_deref = false;
   if (btype) {
     loadResultType = btype;
     Btype *tctyp = circularTypeLoadConversion(expr->btype());
@@ -600,6 +590,21 @@
       space = genCircularConversion(pointer_type(tctyp), expr, loc);
       loadResultType = tctyp;
     }
+    int flavor = expr->flavor();
+    // In most cases loadResultType is the same as value llvm type,
+    // or they're both pointers. Still in some cases we need additional
+    // level of indirection. This happens when:
+    // 1. expr is used to access variable, structure field or is a compound
+    //    expression. Those expression's llvm value is alloca or GEP, which
+    //    is actually a pointer to a value, not value itself.
+    // 2. expr is dereference (N_Deref), which has not yet been fully handled
+    //    by the bridge. In such case the load instruction has not yet been
+    //    generated.
+    if (!loadResultType->type()->isPointerTy())
+      needs_extra_deref =
+          ((flavor == N_Deref && expr->varExprPending()) || flavor == N_Var ||
+           flavor == N_StructField || flavor == N_Compound);
+
   } else {
     // Here we are resolving a pending var expression. The LLVM
     // value should already be pointer to the expression type.
@@ -608,18 +613,10 @@
   }
 
   llvm::Value *spaceVal = space->value();
-  if (spaceVal == nil_pointer_expression()->value()) {
-    llvm::Function *dummyFcn = errorFunction_->function();
-    BlockLIRBuilder builder(dummyFcn, this);
-    llvm::Type *spaceTyp = llvm::PointerType::get(loadResultType->type(), addressSpace_);
-    std::string tag(namegen("cast"));
-    spaceVal = builder.CreateBitCast(spaceVal, spaceTyp, tag);
-    space->appendInstructions(builder.instructions());
-  }
-
-  llvm::PointerType *llpt =
-      llvm::cast<llvm::PointerType>(spaceVal->getType());
-  llvm::Type *llrt = llpt->getElementType();
+  llvm::Type *llrt = loadResultType->type();
+  if (needs_extra_deref)
+    llrt = llvm::PointerType::get(
+        llrt, expr->btype()->type()->getPointerAddressSpace());
 
   // If this type meets our criteria (composite/aggregate whose
   // size is above a certain threshhold) then assume that the
@@ -631,13 +628,11 @@
     std::string ldname(tag);
     ldname += ".ld";
     ldname = namegen(ldname);
-    llvm::Type *vt = spaceVal->getType()->getPointerElementType();
     llvm::Instruction *insBefore = nullptr;
-    llvm::Align ldAlign = datalayout_->getABITypeAlign(vt);
+    llvm::Align ldAlign = datalayout_->getABITypeAlign(llrt);
     bool isVolatile = false;
-    llvm::Instruction *loadInst = new llvm::LoadInst(vt, spaceVal, ldname,
-                                                     isVolatile, ldAlign,
-                                                     insBefore);
+    llvm::Instruction *loadInst = new llvm::LoadInst(
+        llrt, spaceVal, ldname, isVolatile, ldAlign, insBefore);
     rval = nbuilder_.mkDeref(loadResultType, loadInst, space, loc);
     rval->appendInstruction(loadInst);
   } else {
@@ -879,17 +874,6 @@
   // Decide whether we want a simple store instruction or a memcpy.
   if (! useCopyForLoadStore(srcType)) {
 
-    if (srcVal->getType()->isPointerTy()) {
-      llvm::PointerType *dstpt =
-          llvm::cast<llvm::PointerType>(dstType);
-      srcVal = convertForAssignment(srcType, srcVal,
-                                    dstpt->getElementType(), builder);
-    }
-
-    // At this point the types should agree
-    llvm::PointerType *dpt = llvm::cast<llvm::PointerType>(dstType);
-    assert(srcVal->getType() == dpt->getElementType());
-
     // Create and return store
     return builder->CreateStore(srcVal, dstLoc);
   }
@@ -1101,21 +1085,6 @@
 
   // Normal case
   llvm::Value *varval = var->value();
-
-  // Special case for zero-sized globals. These require a type conversion,
-  // since the underlying definition has been coerced to something with
-  // non-zero size (as a means of avoiding linker misbehavior).
-  Btype *underlyingType = var->underlyingType();
-  if (underlyingType != nullptr) {
-    LIRBuilder irbuilder(context_, llvm::ConstantFolder());
-    std::string tag(namegen("zeroSizeCast"));
-    llvm::Type *toType = llvm::PointerType::get(var->btype()->type(),
-                                                addressSpace_);
-    llvm::Value *bitcast =
-        irbuilder.CreateBitCast(var->value(), toType, tag);
-    varval = bitcast;
-  }
-
   Bexpression *varexp = nbuilder_.mkVar(var, varval, location);
   varexp->setTag(var->name().c_str());
   return varexp;
@@ -1172,13 +1141,13 @@
   BIntegerType *bit = btype->castToBIntegerType();
   if (bit->isUnsigned()) {
     uint64_t val = checked_convert_mpz_to_int<uint64_t>(mpz_val);
-    llvm::APInt apiv(bit->bits(), val);
+    llvm::APInt apiv(bit->bits(), val, false, true);
     llvm::Constant *lval = llvm::ConstantInt::get(btype->type(), apiv);
     Bexpression *bconst = nbuilder_.mkConst(btype, lval);
     return makeGlobalExpression(bconst, lval, btype, Location());
   } else {
     int64_t val = checked_convert_mpz_to_int<int64_t>(mpz_val);
-    llvm::APInt apiv(bit->bits(), val, true);
+    llvm::APInt apiv(bit->bits(), val, true, true);
     llvm::Constant *lval = llvm::ConstantInt::get(btype->type(), apiv);
     Bexpression *bconst = nbuilder_.mkConst(btype, lval);
     return makeGlobalExpression(bconst, lval, btype, Location());
@@ -1310,11 +1279,9 @@
                     MV_SkipDebug, llvm::GlobalValue::PrivateLinkage,
                     scon, 1);
   llvm::Constant *varval = llvm::cast<llvm::Constant>(svar->value());
-  llvm::Constant *bitcast =
-      llvm::ConstantExpr::getBitCast(varval, stringType()->type());
-  Bexpression *bconst = nbuilder_.mkConst(stringType(), bitcast);
+  Bexpression *bconst = nbuilder_.mkConst(stringType(), varval);
   Bexpression *rval =
-      makeGlobalExpression(bconst, bitcast, stringType(), Location());
+      makeGlobalExpression(bconst, varval, stringType(), Location());
   stringConstantMap_[scon] = rval;
   return rval;
 }
@@ -2159,7 +2126,7 @@
   llvm::Value *old = nullptr;
   if (glob && glob->getLinkage() == linkage) {
     // A global variable with same name already exists.
-    if (glob->getType()->getElementType() == btype->type()) {
+    if (glob->getValueType() == btype->type()) {
       if (isExtInit == MV_NotExternallyInitialized) {
         // A definition overrides a declaration for external var.
         glob->setExternallyInitialized(false);
@@ -2190,16 +2157,12 @@
         // Here we are creating an external declaration
         // of a different type. We make a bitcast to the
         // new type.
-        llvm::Constant *decl = module_->getOrInsertGlobal(gname, btype->type());
-        bool addressTaken = true; // for now
-        Bvariable *bv =
-            new Bvariable(btype, location, gname, GlobalVar, addressTaken, decl);
-        assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
-        valueVarMap_[bv->value()] = bv;
-        if (genDebug == MV_GenDebug && dibuildhelper() && !errorCount_) {
-          bool exported = (linkage == llvm::GlobalValue::ExternalLinkage);
-          dibuildhelper()->processGlobal(bv, exported);
-        }
+
+        // Starting from LLVM-17 we're no longer using bitcasts
+        // for pointer types
+        auto it = valueVarMap_.find(glob);
+        assert(it != valueVarMap_.end());
+        Bvariable *bv = it->second;
         return bv;
       }
     }
@@ -2239,8 +2202,7 @@
   // Fix up old declaration if there is one
   if (old) {
     assert(llvm::isa<llvm::PointerType>(old->getType()));
-    llvm::Type *declTyp =
-        llvm::cast<llvm::PointerType>(old->getType())->getElementType();
+    llvm::Type *declTyp = llvm::cast<llvm::GlobalValue>(old)->getValueType();
     llvm::Constant *newDecl = module_->getOrInsertGlobal(gname, declTyp);
     old->replaceAllUsesWith(newDecl);
     // NB: previously we had a call to old->deleteValue() here, but this
@@ -2534,7 +2496,7 @@
   // A global with the same name already declared?
   llvm::GlobalVariable *glob = module_->getGlobalVariable(gname);
   if (glob) {
-    assert(glob->getType()->getElementType() == btype->type());
+    assert(glob->getValueType() == btype->type());
     auto it = valueVarMap_.find(glob);
     assert(it != valueVarMap_.end());
     Bvariable *bv = it->second;
@@ -2784,17 +2746,17 @@
          fns == "runtime.mapaccess2_fast64" ||
          fns == "runtime.mapaccess2_faststr" ||
          fns == "runtime.mapaccess2_fat"))
-      fcn->addFnAttr(llvm::Attribute::ReadOnly);
+      fcn->setOnlyReadsMemory();
 
     // memcmp-like.
     if (fns == "runtime.memequal" ||
         fns == "runtime.cmpstring") {
-      fcn->addFnAttr(llvm::Attribute::ReadOnly);
-      fcn->addFnAttr(llvm::Attribute::ArgMemOnly);
+      fcn->setOnlyReadsMemory();
+      fcn->setOnlyAccessesArgMemory();
     }
 
     if (fns == "runtime.memclrNoHeapPointers")
-      fcn->addFnAttr(llvm::Attribute::ArgMemOnly);
+      fcn->setOnlyAccessesArgMemory();
 
     // These functions are called in unlikely branches. But they
     // themselves are not actually cold in the runtime. So only
@@ -3152,10 +3114,7 @@
   // possibly-excepting call with landing pad.
   llvm::SmallVector<llvm::Value *, 8> args(call->arg_begin(), call->arg_end());
   llvm::Value *callop = call->getCalledOperand();
-  llvm::PointerType *pft =
-      llvm::cast<llvm::PointerType>(callop->getType());
-  llvm::FunctionType *fty =
-      llvm::cast<llvm::FunctionType>(pft->getElementType());
+  llvm::FunctionType *fty = call->getFunctionType();
   llvm::InvokeInst *invcall =
       llvm::InvokeInst::Create(fty, callop, contbb, padbb, args,
                                call->getName());
@@ -3284,7 +3243,7 @@
       changed = true;
     if (dibuildhelper_)
       dibuildhelper_->processExprInst(containingStmt, expr, inst);
-    curblock->getInstList().push_back(inst);
+    inst->insertBefore(*curblock, curblock->end());
     curblock = pair.second;
     newinsts.push_back(inst);
 
@@ -3294,7 +3253,7 @@
       // current block.
       LIRBuilder builder(context_, llvm::ConstantFolder());
       llvm::Instruction *unreachable = builder.CreateUnreachable();
-      curblock->getInstList().push_back(unreachable);
+      unreachable->insertBefore(*curblock, curblock->end());
       curblock = nullptr;
       changed = true;
 
@@ -3370,7 +3329,7 @@
     else {
       LIRBuilder builder(context_, llvm::ConstantFolder());
       llvm::Instruction *unreachable = builder.CreateUnreachable();
-      tsucc->getInstList().push_back(unreachable);
+      unreachable->insertBefore(*tsucc, tsucc->end());
     }
   }
 
@@ -3383,7 +3342,7 @@
       else {
         LIRBuilder builder(context_, llvm::ConstantFolder());
         llvm::Instruction *unreachable = builder.CreateUnreachable();
-        fsucc->getInstList().push_back(unreachable);
+        unreachable->insertBefore(*fsucc, fsucc->end());
       }
     }
   }
@@ -3578,12 +3537,12 @@
       if (&inst == term)
         break; // terminator is handled below
       llvm::Instruction *c = cloneInstruction(&inst, argMap);
-      curblock->getInstList().push_back(c);
+      c->insertBefore(*curblock, curblock->end());
     }
     if (llvm::isa<llvm::InvokeInst>(term)) {
       // This is the call to DEFERRETURN. Copy this then we're done.
       llvm::Instruction *c = cloneInstruction(term, argMap);
-      curblock->getInstList().push_back(c);
+      c->insertBefore(*curblock, curblock->end());
       break;
     }
     // By construction it should be linear code.
@@ -3736,7 +3695,8 @@
   // Populate resume block
   builder.SetInsertPoint(finResBB);
   std::string ename(be_->namegen("excv"));
-  llvm::LoadInst *exload = builder.CreateLoad(extmp->getType()->getPointerElementType(), extmp, ename);
+  llvm::Type *vty = llvm::cast<llvm::AllocaInst>(extmp)->getAllocatedType();
+  llvm::LoadInst *exload = builder.CreateLoad(vty, extmp, ename);
   builder.CreateResume(exload);
 
   return finRetBB;
@@ -4021,7 +3981,7 @@
       llvm::Value *zv = llvm::Constant::getNullValue(rtyp);
       ri = builder.CreateRet(zv);
     }
-    epilog->getInstList().push_back(ri);
+    ri->insertBefore(*epilog, epilog->end());
   }
 }
 
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index cd4d855..78602e3 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -65,7 +65,7 @@
 struct GenCallState;
 
 #include "llvm/IR/GlobalValue.h"
-#include "llvm/ADT/Triple.h"
+#include "llvm/TargetParser/Triple.h"
 
 //
 // LLVM-specific implementation of the Backend class; the code in
@@ -487,7 +487,7 @@
                                       Location location);
 
   // Field GEP helper
-  llvm::Value *makeFieldGEP(unsigned fieldIndex,
+  llvm::Value *makeFieldGEP(unsigned fieldIndex, llvm::Type *sty,
                             llvm::Value *sptr);
 
   // Array indexing GEP helper
@@ -496,8 +496,7 @@
                                  llvm::Value *sptr);
 
   // Pointer indexing GEP helper
-  llvm::Value *makePointerOffsetGEP(llvm::PointerType *pt,
-                                    llvm::Value *idx,
+  llvm::Value *makePointerOffsetGEP(Btype *pt, llvm::Value *idx,
                                     llvm::Value *sptr);
 
   // Assignment helper
@@ -614,39 +613,6 @@
   // error statement, returning TRUE if so.
   bool stmtVectorHasError(const std::vector<Bstatement *> &stmts);
 
-  // Converts value "src" for assignment to container of type
-  // "dstType" in assignment-like contexts. This helper exists to help
-  // with cases where the frontend is creating an assignment of form
-  // "X = Y" where X and Y's types are considered matching by the
-  // front end, but are non-matching in an LLVM context. For example,
-  //
-  //   type Ifi func(int) int
-  //   ...
-  //   var fp Ifi = myfunctionfoobar
-  //
-  // Here the right hand side will come out as pointer-to-descriptor,
-  // whereas variable "fp" will have type "pointer to functon", which are
-  // not the same. Another example is assignments involving nil, e.g.
-  //
-  //   var ip *float32
-  //   ...
-  //   ip = nil
-  //
-  // The type of the right hand side of the assignment will be a
-  // generic "*i64" as opposed to "*float32", since the backend
-  // "nil_pointer_expression" does not allow for creation of nil
-  // pointers of specific types.
-  //
-  // Return value will be a new convert Bexpression if a convert is
-  // needed, NULL otherwise.
-  llvm::Value *convertForAssignment(Bexpression *src,
-                                    llvm::Type *dstToType);
-  // lower-level version of the routine above
-  llvm::Value *convertForAssignment(Btype *srcType,
-                                    llvm::Value *srcVal,
-                                    llvm::Type *dstToType,
-                                    BlockLIRBuilder *builder);
-
 
   // Apply type conversion for a binary operation. This helper exists
   // to resolve situations where expressions are created by the front
diff --git a/bridge/go-sha1.cpp b/bridge/go-sha1.cpp
index 54925d5..e978c8e 100644
--- a/bridge/go-sha1.cpp
+++ b/bridge/go-sha1.cpp
@@ -47,7 +47,8 @@
 std::string
 Llvm_Sha1_Helper::finish()
 {
-  std::string result(ctx_->final().str(), 0, checksum_len);
+  auto arr = ctx_->final();
+  std::string result(std::begin(arr), std::begin(arr) + checksum_len);
   return result;
 }
 
diff --git a/driver-main/llvm-goc.cpp b/driver-main/llvm-goc.cpp
index 472dc2f..f635e00 100644
--- a/driver-main/llvm-goc.cpp
+++ b/driver-main/llvm-goc.cpp
@@ -26,7 +26,6 @@
 #include "Tool.h"
 
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Triple.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
 #include "llvm/IR/DiagnosticInfo.h"
@@ -35,7 +34,6 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Verifier.h"
-#include "llvm/MC/SubtargetFeature.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
@@ -43,8 +41,8 @@
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
-#include "llvm/Support/Format.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
@@ -56,8 +54,9 @@
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
+#include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/IPO.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 
 #include <algorithm>
 #include <cstring>
@@ -95,7 +94,7 @@
   const char *progname = argv[0];
 
   unsigned missingArgIndex, missingArgCount;
-  ArrayRef<const char *> argvv = makeArrayRef(argv, argc);
+  ArrayRef<const char *> argvv(argv, argc);
   args_ = opts_->ParseArgs(argvv.slice(1), missingArgIndex, missingArgCount);
 
   // Honor --help first
@@ -172,9 +171,8 @@
   // that standard input be empty (so as to support the Go tool, but
   // not act as a general-purposes C compiler).
   opt::Arg *xarg = args_.getLastArg(gollvm::options::OPT_x);
-  if (xarg != nullptr &&
-      ! llvm::StringRef(xarg->getValue()).equals("c") &&
-      ! llvm::StringRef(xarg->getValue()).equals("go")) {
+  if (xarg != nullptr && llvm::StringRef(xarg->getValue()) != "c" &&
+      llvm::StringRef(xarg->getValue()) != "go") {
     errs() << progname << ": invalid argument '"
            << xarg->getValue() << "' to '"
            << xarg->getAsString(args_) << "' option\n";
diff --git a/driver/ArchCpuSetup.cpp b/driver/ArchCpuSetup.cpp
index a7eb765..bf91983 100644
--- a/driver/ArchCpuSetup.cpp
+++ b/driver/ArchCpuSetup.cpp
@@ -13,8 +13,8 @@
 #include "ArchCpuSetup.h"
 
 #include "llvm/Option/Arg.h"
-#include "llvm/Support/Host.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
 
 namespace gollvm { namespace arch {
 #include "ArchCpusAttrs.h"
diff --git a/driver/Command.cpp b/driver/Command.cpp
index 2f10e8c..956108a 100644
--- a/driver/Command.cpp
+++ b/driver/Command.cpp
@@ -36,13 +36,11 @@
   argv.reserve(n);
   for (size_t i = 0; i < n; ++i)
     argv.push_back(arguments_[i]);
-  return llvm::sys::ExecuteAndWait(executable_,
-                                   argv,
-                                   /*env=*/llvm::None,
-                                   /*Redirects*/{},
+  return llvm::sys::ExecuteAndWait(executable_, argv,
+                                   /*env=*/{},
+                                   /*Redirects*/ {},
                                    /*secondsToWait=*/0,
-                                   /*memoryLimit=*/0,
-                                   errMsg);
+                                   /*memoryLimit=*/0, errMsg);
 }
 
 void Command::print(llvm::raw_ostream &os, bool quoteArgs)
diff --git a/driver/Compilation.cpp b/driver/Compilation.cpp
index 4123e40..6abf2f0 100644
--- a/driver/Compilation.cpp
+++ b/driver/Compilation.cpp
@@ -127,8 +127,8 @@
   return newFileArtifact(ofn.c_str(), false);
 }
 
-llvm::Optional<Artifact*> Compilation::createTemporaryFileArtifact(Action *act)
-{
+std::optional<Artifact *>
+Compilation::createTemporaryFileArtifact(Action *act) {
   llvm::SmallString<128> tempFileName;
   std::error_code tfcEC =
       llvm::sys::fs::createTemporaryFile(act->getName(),
@@ -137,7 +137,7 @@
   if (tfcEC) {
     llvm::errs() << driver_.progname() << ": error: "
                  << tfcEC.message() << "\n";
-    return llvm::None;
+    return {};
   }
   return newFileArtifact(tempFileName.c_str(), true);
 }
diff --git a/driver/Compilation.h b/driver/Compilation.h
index ec96bc8..4861b60 100644
--- a/driver/Compilation.h
+++ b/driver/Compilation.h
@@ -45,9 +45,9 @@
   // Generate a new temp file and return an artifact for it. Here
   // llvm::Optional is used in case the temp file creation fails for
   // some reason.
-  llvm::Optional<Artifact*> createTemporaryFileArtifact(Action *act);
+  std::optional<Artifact *> createTemporaryFileArtifact(Action *act);
 
-    // Create new artifact based on file name. If 'isTempfile' is set,
+  // Create new artifact based on file name. If 'isTempfile' is set,
   // the file should be scheduled for deletion after compilation finishes.
   Artifact *newFileArtifact(const char *path, bool isTempFile);
 
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 26af4e7..3601a36 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -41,25 +41,24 @@
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
-#include "llvm/IR/IRPrintingPasses.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Verifier.h"
-#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/IRPrinter/IRPrintingPasses.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/StandardInstrumentations.h"
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Remarks/YAMLRemarkSerializer.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Format.h"
-#include "llvm/Support/Host.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
@@ -70,11 +69,14 @@
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
 #include "llvm/Transforms/IPO.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 #include "llvm/Transforms/Utils.h"
+#include "llvm/Transforms/Utils/AddDiscriminators.h"
 
 #include <sstream>
 
@@ -111,8 +113,10 @@
   const char *progname_;
   std::string executablePath_;
   opt::InputArgList &args_;
-  CodeGenOpt::Level cgolvl_;
-  unsigned olvl_;
+  CodeGenOptLevel cgolvl_;
+  OptimizationLevel olvl_;
+  PipelineTuningOptions pto_;
+  std::optional<PGOOptions> pgo_;
   bool hasError_;
   std::unique_ptr<Llvm_backend> bridge_;
   std::unique_ptr<TargetMachine> target_;
@@ -129,8 +133,6 @@
   std::string sampleProfileFile_;
   bool enable_gc_;
 
-  void createPasses(legacy::PassManager &MPM,
-                    legacy::FunctionPassManager &FPM);
   void setupGoSearchPath();
   void setCConv();
 
@@ -148,22 +150,13 @@
   bool enableVectorization(bool slp);
 };
 
-CompileGoImpl::CompileGoImpl(CompileGo &cg,
-                             ToolChain &tc,
+CompileGoImpl::CompileGoImpl(CompileGo &cg, ToolChain &tc,
                              const std::string &executablePath)
-    : cg_(cg),
-      triple_(tc.driver().triple()),
-      toolchain_(tc),
-      driver_(tc.driver()),
-      cconv_(CallingConvId::MaxID),
-      progname_(tc.driver().progname()),
-      executablePath_(executablePath),
-      args_(tc.driver().args()),
-      cgolvl_(CodeGenOpt::Default),
-      olvl_(2),
-      hasError_(false),
-      enable_gc_(false)
-{
+    : cg_(cg), triple_(tc.driver().triple()), toolchain_(tc),
+      driver_(tc.driver()), cconv_(CallingConvId::MaxID),
+      progname_(tc.driver().progname()), executablePath_(executablePath),
+      args_(tc.driver().args()), cgolvl_(CodeGenOptLevel::Default),
+      olvl_(OptimizationLevel::O2), hasError_(false), enable_gc_(false) {
   InitializeAllTargets();
   InitializeAllTargetMCs();
   InitializeAllAsmPrinters();
@@ -311,21 +304,28 @@
     }
     switch (lev[0]) {
       case '0':
-        olvl_ = 0;
-        cgolvl_ = CodeGenOpt::None;
+        olvl_ = OptimizationLevel::O0;
+        cgolvl_ = CodeGenOptLevel::None;
         break;
       case '1':
-        olvl_ = 1;
-        cgolvl_ = CodeGenOpt::Less;
+        olvl_ = OptimizationLevel::O1;
+        cgolvl_ = CodeGenOptLevel::Less;
         break;
-      case 's': // TODO: -Os same as -O for now.
+      case 'z':
+        olvl_ = OptimizationLevel::Oz;
+        cgolvl_ = CodeGenOptLevel::Less;
+        break;
+      case 's':
+        olvl_ = OptimizationLevel::Os;
+        cgolvl_ = CodeGenOptLevel::Less;
+        break;
       case '2':
-        olvl_ = 2;
-        cgolvl_ = CodeGenOpt::Default;
+        olvl_ = OptimizationLevel::O2;
+        cgolvl_ = CodeGenOptLevel::Default;
         break;
       case '3':
-        olvl_ = 3;
-        cgolvl_ = CodeGenOpt::Aggressive;
+        olvl_ = OptimizationLevel::O3;
+        cgolvl_ = CodeGenOptLevel::Aggressive;
         break;
       default:
         errs() << progname_ << ": invalid optimization level.\n";
@@ -333,11 +333,18 @@
     }
   }
 
-  go_no_warn = args_.hasArg(gollvm::options::OPT_w);
-  go_loc_show_column =
-      driver_.reconcileOptionPair(gollvm::options::OPT_fshow_column,
-                                  gollvm::options::OPT_fno_show_column,
-                                  true);
+  pto_.LoopVectorization = enableVectorization(false);
+  pto_.SLPVectorization = enableVectorization(true);
+  pto_.LoopUnrolling = driver_.reconcileOptionPair(gollvm::options::OPT_funroll_loops,
+                                                   gollvm::options::OPT_fno_unroll_loops, true);
+  pto_.LoopInterleaving = driver_.reconcileOptionPair(gollvm::options::OPT_finterleave_loops,
+                                                      gollvm::options::OPT_fno_interleave_loops, true);
+  // Merge identical functions at the LLVM IR level.
+  pto_.MergeFunctions = driver_.reconcileOptionPair(gollvm::options::OPT_fmerge_functions,
+                                                    gollvm::options::OPT_fno_merge_functions, false);
+  // Provide .cgoprofile section for lld to order functions.
+  pto_.CallGraphProfile = driver_.reconcileOptionPair(gollvm::options::OPT_fcg_profile,
+                                                      gollvm::options::OPT_fno_cg_profile, false);
 
   // AutoFDO.
   opt::Arg *sprofarg =
@@ -365,6 +372,29 @@
     }
   }
 
+  pto_.LoopVectorization = enableVectorization(false);
+  pto_.SLPVectorization = enableVectorization(true);
+  pto_.LoopUnrolling =
+      driver_.reconcileOptionPair(gollvm::options::OPT_funroll_loops,
+                                  gollvm::options::OPT_fno_unroll_loops, true);
+  pto_.LoopInterleaving = driver_.reconcileOptionPair(
+      gollvm::options::OPT_finterleave_loops,
+      gollvm::options::OPT_fno_interleave_loops, true);
+  // Merge identical functions at the LLVM IR level.
+  pto_.MergeFunctions = driver_.reconcileOptionPair(
+      gollvm::options::OPT_fmerge_functions,
+      gollvm::options::OPT_fno_merge_functions, false);
+  // Provide .cgoprofile section for lld to order functions.
+  pto_.CallGraphProfile =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fcg_profile,
+                                  gollvm::options::OPT_fno_cg_profile, false);
+
+  go_no_warn = args_.hasArg(gollvm::options::OPT_w);
+  go_loc_show_column =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fshow_column,
+                                  gollvm::options::OPT_fno_show_column,
+                                  true);
+
   // Capture optimization record.
   opt::Arg *optrecordarg =
       args_.getLastArg(gollvm::options::OPT_fsave_optimization_record,
@@ -420,7 +450,7 @@
   llvm::DebugCompressionType dct = llvm::DebugCompressionType::None;
   if (!driver_.determineDebugCompressionType(&dct))
     return false;
-  Options.CompressDebugSections = dct;
+  Options.MCOptions.CompressDebugSections = dct;
 
   // FIXME: this hard-wires on the equivalent of -ffunction-sections
   // and -fdata-sections, since there doesn't seem to be a high-level
@@ -473,7 +503,7 @@
     return false;
 
   // Create target machine
-  Optional<llvm::CodeModel::Model> CM = None;
+  std::optional<llvm::CodeModel::Model> CM = {};
   target_.reset(
       TheTarget->createTargetMachine(triple_.getTriple(),
                                      targetCpuAttr_, targetFeaturesAttr_,
@@ -497,7 +527,7 @@
       std::make_unique<BEDiagnosticHandler>(&this->hasError_,
                                             this->remarkCtl_));
 
-  llvm::Optional<unsigned> enable_gc =
+  std::optional<unsigned> enable_gc =
       driver_.getLastArgAsInteger(gollvm::options::OPT_enable_gc_EQ, 0u);
   enable_gc_ = enable_gc && *enable_gc;
 
@@ -523,7 +553,7 @@
   bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get(), addrspace, triple_, cconv_));
 
   // Honor inline, tracelevel cmd line options
-  llvm::Optional<unsigned> tl =
+  std::optional<unsigned> tl =
       driver_.getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
   if (!tl)
     return false;
@@ -601,7 +631,7 @@
                                   true);
   args.compiling_runtime =
       args_.hasArg(gollvm::options::OPT_fgo_compiling_runtime);
-  llvm::Optional<int> del =
+  std::optional<int> del =
       driver_.getLastArgAsInteger(gollvm::options::OPT_fgo_debug_escape_EQ, 0);
   if (!del)
     return false;
@@ -759,7 +789,7 @@
     bridge_->dumpModule();
   if (!args_.hasArg(gollvm::options::OPT_noverify) && !go_be_saw_errors())
     bridge_->verifyModule();
-  llvm::Optional<unsigned> tl =
+  std::optional<unsigned> tl =
       driver_.getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
   if (*tl)
     std::cerr << "linemap stats:" << linemap_->statistics() << "\n";
@@ -780,7 +810,7 @@
 
 bool CompileGoImpl::enableVectorization(bool slp)
 {
-  bool enable = (olvl_ > 1);
+  bool enable = (olvl_.getSpeedupLevel() > 1);
   if (slp)
     return driver_.reconcileOptionPair(gollvm::options::OPT_fslp_vectorize,
                                        gollvm::options::OPT_fno_slp_vectorize,
@@ -791,108 +821,116 @@
                                        enable);
 }
 
-static void addAddDiscriminatorsPass(const llvm::PassManagerBuilder &Builder,
-                                     llvm::legacy::PassManagerBase &PM) {
-  PM.add(createAddDiscriminatorsPass());
-}
-
-void CompileGoImpl::createPasses(legacy::PassManager &MPM,
-                                 legacy::FunctionPassManager &FPM)
+bool CompileGoImpl::invokeBackEnd(const Action &jobAction)
 {
-  if (args_.hasArg(gollvm::options::OPT_disable_llvm_passes))
-    return;
+  LoopAnalysisManager lam;
+  FunctionAnalysisManager fam;
+  CGSCCAnalysisManager gcam;
+  ModuleAnalysisManager mam;
 
-  // FIXME: support LTO, ThinLTO, PGO
+  bool debugPassStructure = false;
+  bool debugPassManager = args_.hasArg(gollvm::options::OPT_fdebug_pass_manager);
+  bool verifyEach = args_.hasArg(gollvm::options::OPT_fverify_each);
+  PassInstrumentationCallbacks pic;
+  PrintPassOptions printPassOpts;
+  printPassOpts.Indent = debugPassStructure;
+  printPassOpts.SkipAnalyses = debugPassStructure;
+  StandardInstrumentations si(context_, debugPassManager || debugPassStructure,
+                              verifyEach, printPassOpts);
 
-  PassManagerBuilder pmb;
+  // TODO: Maybe better to keep some kind of pass table.
+  pic.addClassToPassName("GoStatepoints", "go-statepoints");
+  pic.addClassToPassName("GoSafeGetgPass", "go-safegetg");
+  pic.addClassToPassName("RemoveAddrSpacePass", "remove-addrspacecast");
 
-  // Configure the inliner
-  if (args_.hasArg(gollvm::options::OPT_fno_inline) || olvl_ == 0) {
-    // Nothing here at the moment. There is go:noinline, but no equivalent
-    // of go:alwaysinline.
-  } else {
-    bool disableInlineHotCallSite = false; // for autofdo, not yet supported
-    pmb.Inliner =
-        createFunctionInliningPass(olvl_, 2, disableInlineHotCallSite);
+  si.registerCallbacks(pic);
+
+  PassBuilder pb(target_.get(), pto_, pgo_, &pic);
+
+  // Register the target library analysis directly and give it a customized
+  // preset TLI.
+  tlii_.reset(new TargetLibraryInfoImpl(triple_));
+  fam.registerPass([this] { return TargetLibraryAnalysis(*tlii_); });
+
+  // Register all the basic analyses with the managers.
+  pb.registerModuleAnalyses(mam);
+  pb.registerCGSCCAnalyses(gcam);
+  pb.registerFunctionAnalyses(fam);
+  pb.registerLoopAnalyses(lam);
+  pb.crossRegisterProxies(lam, fam, gcam, mam);
+
+  bool disablePasses =  args_.hasArg(gollvm::options::OPT_disable_llvm_passes);
+  ThinOrFullLTOPhase lto_phase = ThinOrFullLTOPhase::None;
+
+  ModulePassManager modulePasses;
+  if (!disablePasses) {
+    if (olvl_ == OptimizationLevel::O0) {
+
+      modulePasses = pb.buildO0DefaultPipeline(olvl_, lto_phase);
+    } else if (lto_phase == ThinOrFullLTOPhase::ThinLTOPreLink) {
+      modulePasses = pb.buildThinLTOPreLinkDefaultPipeline(olvl_);
+    } else if (lto_phase == ThinOrFullLTOPhase::FullLTOPreLink) {
+      modulePasses = pb.buildLTOPreLinkDefaultPipeline(olvl_);
+    } else {
+      modulePasses = pb.buildPerModuleDefaultPipeline(olvl_);
+    }
   }
 
-  pmb.OptLevel = olvl_;
-  pmb.SizeLevel = 0; // TODO: decide on right value here
-  pmb.PrepareForThinLTO = false;
-  pmb.PrepareForLTO = false;
-  pmb.SLPVectorize = enableVectorization(true);
-  pmb.LoopVectorize = enableVectorization(false);
+  bool needDwarfDiscr = !sampleProfileFile_.empty();
 
-  bool needDwarfDiscr = false;
-  if (! sampleProfileFile_.empty()) {
-    pmb.PGOSampleUse = sampleProfileFile_;
-    needDwarfDiscr = true;
-  }
   opt::Arg *dbgprofarg =
       args_.getLastArg(gollvm::options::OPT_fdebug_info_for_profiling,
                        gollvm::options::OPT_fno_debug_info_for_profiling);
   if (dbgprofarg) {
-    if (dbgprofarg->getOption().matches(gollvm::options::OPT_fdebug_info_for_profiling))
+    if (dbgprofarg->getOption().matches(
+            gollvm::options::OPT_fdebug_info_for_profiling))
       needDwarfDiscr = true;
     else
       needDwarfDiscr = false;
   }
-  if (needDwarfDiscr)
-    pmb.addExtension(llvm::PassManagerBuilder::EP_EarlyAsPossible,
-                           addAddDiscriminatorsPass);
-
-
-  FPM.add(new TargetLibraryInfoWrapperPass(*tlii_));
-  if (! args_.hasArg(gollvm::options::OPT_noverify))
-    FPM.add(createVerifierPass());
-
-  pmb.populateFunctionPassManager(FPM);
-  pmb.populateModulePassManager(MPM);
-}
-
-bool CompileGoImpl::invokeBackEnd(const Action &jobAction)
-{
-  tlii_.reset(new TargetLibraryInfoImpl(triple_));
-
-  // Set up module and function passes
-  legacy::PassManager modulePasses;
-  modulePasses.add(
-      createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
-  legacy::FunctionPassManager functionPasses(module_.get());
-  functionPasses.add(
-      createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
-  createPasses(modulePasses, functionPasses);
+  if (needDwarfDiscr) {
+    pb.registerPipelineStartEPCallback([](llvm::ModulePassManager &MPM,
+                                          llvm::OptimizationLevel) {
+      MPM.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
+    });
+  }
 
   // Disable inlining getg in some cases on x86_64.
   if (triple_.getArch() == llvm::Triple::x86_64) {
-      modulePasses.add(createGoSafeGetgPass());
+    modulePasses.addPass(GoSafeGetgPass());
   }
 
   // Add statepoint insertion pass to the end of optimization pipeline,
   // right before lowering to machine IR.
   if (enable_gc_) {
-    modulePasses.add(createGoStatepointsLegacyPass());
-    modulePasses.add(createRemoveAddrSpacePass(target_->createDataLayout()));
+    modulePasses.addPass(GoStatepoints());
+    modulePasses.addPass(RemoveAddrSpacePass(target_->createDataLayout()));
   }
 
+  // We still use the legacy PM to run the codegen pipeline since the new PM
+  // does not work with the codegen pipeline.
   legacy::PassManager codeGenPasses;
   bool noverify = args_.hasArg(gollvm::options::OPT_noverify);
-  CodeGenFileType ft = (jobAction.type() == Action::A_CompileAndAssemble ?
-                        CGFT_ObjectFile : CGFT_AssemblyFile);
 
   // Add passes to emit bitcode or LLVM IR as appropriate. Here we mimic
   // clang behavior, which is to emit bitcode when "-emit-llvm" is specified
   // but an LLVM IR dump of "-S -emit-llvm" is used.
   raw_pwrite_stream *OS = &asmout_->os();
   if (args_.hasArg(gollvm::options::OPT_emit_llvm)) {
+    if (!noverify) {
+      modulePasses.addPass(VerifierPass());
+    }
     bool bitcode = !args_.hasArg(gollvm::options::OPT_S);
     bool preserveUseLists =
         driver_.reconcileOptionPair(gollvm::options::OPT_emit_llvm_uselists,
                                     gollvm::options::OPT_no_emit_llvm_uselists,
                                     false);
-    modulePasses.add(bitcode ?
-                     createBitcodeWriterPass(*OS, preserveUseLists) :
-                     createPrintModulePass(*OS, "", preserveUseLists));
+    if (bitcode) {
+      modulePasses.addPass(BitcodeWriterPass(*OS, preserveUseLists,
+                                             /* ThinLTO summary */ false));
+    } else {
+      modulePasses.addPass(PrintModulePass(*OS, "", preserveUseLists));
+    }
     goto run;
   }
 
@@ -909,7 +947,11 @@
   // LLVMTargetMachine::addPassesToEmitFile (and its callee).
   // TODO: error check.
   {
-    LLVMTargetMachine *lltm = (LLVMTargetMachine*)(target_.get()); // FIXME: TargetMachine doesn't support llvm::cast?
+    CodeGenFileType ft = (jobAction.type() == Action::A_CompileAndAssemble
+                              ? CodeGenFileType::ObjectFile
+                              : CodeGenFileType::AssemblyFile);
+    llvm::TargetMachine *lltm =
+        target_.get(); // FIXME: TargetMachine doesn't support llvm::cast?
     TargetPassConfig *passConfig = lltm->createPassConfig(codeGenPasses);
     // Set PassConfig options provided by TargetMachine.
     passConfig->setDisableVerify(noverify);
@@ -932,15 +974,8 @@
   }
 
 run:
-  // Here we go... first function passes
-  functionPasses.doInitialization();
-  for (Function &F : *module_.get())
-    if (!F.isDeclaration())
-      functionPasses.run(F);
-  functionPasses.doFinalization();
-
   // ... then module passes
-  modulePasses.run(*module_.get());
+  modulePasses.run(*module_.get(), mam);
 
   // ... and finally code generation
   if (!args_.hasArg(gollvm::options::OPT_emit_llvm))
diff --git a/driver/Distro.cpp b/driver/Distro.cpp
index 2eace0d..0b02411 100644
--- a/driver/Distro.cpp
+++ b/driver/Distro.cpp
@@ -12,7 +12,7 @@
 
 #include "Distro.h"
 
-#include "llvm/Support/Host.h"
+#include "llvm/TargetParser/Host.h"
 
 namespace distro {
 
diff --git a/driver/Distro.h b/driver/Distro.h
index 4bd0a36..086e0e7 100644
--- a/driver/Distro.h
+++ b/driver/Distro.h
@@ -14,7 +14,7 @@
 #define GOLLVM_DRIVER_DISTRO_H
 
 #include "GccUtils.h"
-#include "llvm/ADT/Triple.h"
+#include "llvm/TargetParser/Triple.h"
 
 namespace distro {
 
diff --git a/driver/Driver.cpp b/driver/Driver.cpp
index 777cc2b..4355475 100644
--- a/driver/Driver.cpp
+++ b/driver/Driver.cpp
@@ -11,9 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Host.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
+#include "llvm/TargetParser/Host.h"
 
 #include <sstream>
 
@@ -185,9 +185,9 @@
         value == "--nocompress-debug-sections") {
       continue;
     }
-    if (value.startswith("-compress-debug-sections") ||
-        value.startswith("--compress-debug-sections") ||
-        value.startswith("-march")) {
+    if (value.starts_with("-compress-debug-sections") ||
+        value.starts_with("--compress-debug-sections") ||
+        value.starts_with("-march")) {
       continue;
     }
     // Unrecognized -Wa,... option
@@ -203,11 +203,11 @@
                                                llvm::DebugCompressionType *dct,
                                                const char *which)
 {
-  if (ga == "zlib") {
-    *dct = llvm::DebugCompressionType::Z;
+  if (ga == "zlib" || ga == "zlib-gnu") {
+    *dct = llvm::DebugCompressionType::Zlib;
     return dct;
-  } else if (ga == "zlib-gnu") {
-    *dct = llvm::DebugCompressionType::GNU;
+  } else if (ga == "zstd") {
+    *dct = llvm::DebugCompressionType::Zstd;
     return dct;
   } else if (ga == "none") {
     *dct = llvm::DebugCompressionType::None;
@@ -231,7 +231,7 @@
                                             gollvm::options::OPT_Wa_COMMA,
                                             gollvm::options::OPT_Xassembler)) {
     if (arg->getOption().matches(gollvm::options::OPT_gz)) {
-      *dct = llvm::DebugCompressionType::GNU;
+      *dct = llvm::DebugCompressionType::Zlib;
     } else if (arg->getOption().matches(gollvm::options::OPT_gz_EQ)) {
       auto value = llvm::StringRef(arg->getValue());
      if (gzArgToDCT(value, dct, "-gz=") == nullptr)
@@ -242,15 +242,15 @@
       if (value == "-nocompress-debug-sections" ||
           value == "--nocompress-debug-sections") {
         *dct = llvm::DebugCompressionType::None;
-      } else if (value.startswith("-compress-debug-sections") ||
-                 value.startswith("--compress-debug-sections")) {
+      } else if (value.starts_with("-compress-debug-sections") ||
+                 value.starts_with("--compress-debug-sections")) {
         const char *wh =
             (arg->getOption().matches(gollvm::options::OPT_Xassembler) ?
              "-Xassembler" : "-Wa,");
         value.consume_front("--compress-debug-sections");
         value.consume_front("-compress-debug-sections");
         auto arg = value;
-        if (value.startswith("="))
+        if (value.starts_with("="))
           arg.consume_front("=");
         else
           arg = "zlib";
@@ -365,20 +365,16 @@
   return false;
 }
 
-Optional<Reloc::Model>
-Driver::reconcileRelocModel()
-{
+std::optional<Reloc::Model> Driver::reconcileRelocModel() {
   auto picLevel = getPicLevel();
   if (picLevel != PICLevel::NotPIC) {
     Reloc::Model R = Reloc::PIC_;
     return R;
   }
-  return None;
+  return {};
 }
 
-Optional<FPOpFusion::FPOpFusionMode>
-Driver::getFPOpFusionMode()
-{
+std::optional<FPOpFusion::FPOpFusionMode> Driver::getFPOpFusionMode() {
   opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_ffp_contract_EQ);
   FPOpFusion::FPOpFusionMode res = FPOpFusion::Standard;
   if (arg != nullptr) {
@@ -393,7 +389,7 @@
       errs() << progname_ << ": invalid argument '"
              << arg->getValue() << "' to '"
              << arg->getAsString(args_) << "' option\n";
-      return None;
+      return {};
     }
   }
   return res;
@@ -504,8 +500,8 @@
       opt::Arg *xarg = args_.getLastArg(gollvm::options::OPT_x);
       assert(xarg);
       const char *suf =
-          (llvm::StringRef(xarg->getValue()).equals("c") ? "c" :
-           (llvm::StringRef(xarg->getValue()).equals("go") ? "go" : "?"));
+          (xarg->containsValue("c") ? "c"
+                                    : (xarg->containsValue("go") ? "go" : "?"));
       act = new ReadStdinAction(suf);
       schedAction = true;
     } else {
diff --git a/driver/Driver.h b/driver/Driver.h
index 3b2813d..6c87d5d 100644
--- a/driver/Driver.h
+++ b/driver/Driver.h
@@ -13,16 +13,15 @@
 #ifndef GOLLVM_DRIVER_DRIVER_H
 #define GOLLVM_DRIVER_DRIVER_H
 
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Triple.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetOptions.h"
+#include "llvm/TargetParser/Triple.h"
 
 #include "Action.h"
 #include "Artifact.h"
@@ -112,14 +111,14 @@
   bool supportedAsmOptions();
   bool determineDebugCompressionType(llvm::DebugCompressionType *dct);
   bool usingSplitStack() const { return usingSplitStack_; }
-  template<typename IT>
-  llvm::Optional<IT> getLastArgAsInteger(gollvm::options::ID id,
-                                         IT defaultValue);
-  llvm::Optional<llvm::Reloc::Model> reconcileRelocModel();
+  template <typename IT>
+  std::optional<IT> getLastArgAsInteger(gollvm::options::ID id,
+                                        IT defaultValue);
+  std::optional<llvm::Reloc::Model> reconcileRelocModel();
   bool reconcileOptionPair(gollvm::options::ID yesOption,
                            gollvm::options::ID noOption,
                            bool defaultVal);
-  llvm::Optional<llvm::FPOpFusion::FPOpFusionMode> getFPOpFusionMode();
+  std::optional<llvm::FPOpFusion::FPOpFusionMode> getFPOpFusionMode();
   typedef llvm::SmallVector<llvm::opt::Arg *, 8> inarglist;
   void appendInputActions(const inarglist &infiles,
                           ActionList &result,
@@ -155,11 +154,9 @@
                                          const char *which);
 };
 
-template<typename IT>
-llvm::Optional<IT>
-Driver::getLastArgAsInteger(gollvm::options::ID id,
-                            IT defaultValue)
-{
+template <typename IT>
+std::optional<IT> Driver::getLastArgAsInteger(gollvm::options::ID id,
+                                              IT defaultValue) {
   IT result = defaultValue;
   llvm::opt::Arg *arg = args_.getLastArg(id);
   if (arg != nullptr) {
@@ -167,7 +164,7 @@
       llvm::errs() << progname_ << ": invalid argument '"
                    << arg->getValue() << "' to '"
                    << arg->getAsString(args_) << "' option\n";
-      return llvm::None;
+      return {};
     }
   }
   return result;
diff --git a/driver/GollvmOptions.cpp b/driver/GollvmOptions.cpp
index 4455b1a..d8697e9 100644
--- a/driver/GollvmOptions.cpp
+++ b/driver/GollvmOptions.cpp
@@ -20,23 +20,35 @@
 #include "GollvmOptions.inc"
 #undef PREFIX
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "GollvmOptions.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_VALUES_CODE
+#include "GollvmOptions.inc"
+#undef OPTTABLE_VALUES_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "GollvmOptions.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_UNION_CODE
+#include "GollvmOptions.inc"
+#undef OPTTABLE_PREFIXES_UNION_CODE
+
 static const OptTable::Info InfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
-               HELPTEXT, METAVAR, VALUES)                                      \
-  {PREFIX, NAME,  HELPTEXT,    METAVAR,     OPT_##ID,  Option::KIND##Class,    \
-   PARAM,  FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
 #include "GollvmOptions.inc"
 #undef OPTION
 };
 
 namespace {
-
-class DriverOptTable : public OptTable {
+class DriverOptTable : public PrecomputedOptTable {
 public:
   DriverOptTable()
-      : OptTable(InfoTable) {}
+      : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                            OptionPrefixesUnion) {}
 };
-
 }
 
 std::unique_ptr<OptTable> createGollvmDriverOptTable() {
diff --git a/driver/GollvmOptions.h b/driver/GollvmOptions.h
index 5ab4477..2a98647 100644
--- a/driver/GollvmOptions.h
+++ b/driver/GollvmOptions.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_GOLLVM_DRIVER_GOLLVMOPTIONS_H
 #define LLVM_GOLLVM_DRIVER_GOLLVMOPTIONS_H
 
+#include "llvm/Option/OptTable.h"
 #include <memory>
 
 namespace llvm {
@@ -31,9 +32,7 @@
 
 enum ID {
     OPT_INVALID = 0, // This is not an option ID.
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
-               HELPTEXT, METAVAR, VALUES)                                      \
-  OPT_##ID,
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
 #include "GollvmOptions.inc"
     LastOption
 #undef OPTION
diff --git a/driver/GollvmOptions.td b/driver/GollvmOptions.td
index 404a1ad..18e0e99 100644
--- a/driver/GollvmOptions.td
+++ b/driver/GollvmOptions.td
@@ -275,6 +275,26 @@
 def fno_slp_vectorize : Flag<["-"], "fno-slp-vectorize">, Group<f_Group>,
   HelpText<"Disable the superword-level parallelism vectorization passes">;
 
+def fmerge_functions : Flag<["-"], "fmerge-functions">, Group<f_Group>,
+    HelpText<"Enable merging identical functions">;
+def fno_merge_functions : Flag<["-"], "fno-merge-functions">, Group<f_Group>,
+    HelpText<"Disable merging identical functions">;
+
+def funroll_loops : Flag<["-"], "funroll-loops">, Group<f_Group>,
+    HelpText<"Enable loop unrolling">;
+def fno_unroll_loops : Flag<["-"], "fno-unroll-loops">, Group<f_Group>,
+    HelpText<"Disable loop unrolling">;
+
+def finterleave_loops : Flag<["-"], "finterleave-loops">, Group<f_Group>,
+    HelpText<"Enable loop interleaving">;
+def fno_interleave_loops : Flag<["-"], "fno-interleave-loops">, Group<f_Group>,
+    HelpText<"Disable loop interleaving">;
+
+def fcg_profile : Flag<["-"], "fcg-profile">, Group<f_Group>,
+    HelpText<"Enable linker re-ordering functions by CG profile (supported by LLD)">;
+def fno_cg_profile : Flag<["-"], "fno-cg-profile">, Group<f_Group>,
+    HelpText<"Disable linker re-ordering functions by CG profile">;
+
 def ffp_contract_EQ : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   HelpText<"Form fused FP ops (e.g. FMAs): fast (everywhere)"
   " | on (according to FP_CONTRACT pragma, default) | off (never fuse)">,
@@ -443,9 +463,6 @@
 def finline_functions : Flag<["-"], "finline-functions">,
     Flags<[Ignored]>, HelpText<"This option is ignored">;
 
-def funroll_loops : Flag<["-"], "funroll-loops">,
-    Flags<[Ignored]>, HelpText<"This option is ignored">;
-
 def pedantic_errors : Flag<["-"], "pedantic-errors">,
     Flags<[Ignored]>, HelpText<"This option is ignored">;
 
@@ -519,3 +536,9 @@
 
 def enable_gc_EQ : Joined<["-"], "enable-gc=">,
   HelpText<"Enable stack map generation">;
+
+def fdebug_pass_manager : Flag<["-"], "fdebug-pass-manager">,
+  HelpText<"Print passes run and other information for debugging the pass manager">;
+
+def fverify_each : Flag<["-"], "fverify-each">,
+  HelpText<"Run verification after each pass in new pass manager">;
diff --git a/driver/IntegAssembler.cpp b/driver/IntegAssembler.cpp
index a40be9f..0cdea73 100644
--- a/driver/IntegAssembler.cpp
+++ b/driver/IntegAssembler.cpp
@@ -27,7 +27,6 @@
 #include "Driver.h"
 #include "ToolChain.h"
 
-#include "llvm/ADT/Triple.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/MC/MCAsmBackend.h"
@@ -52,7 +51,6 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Host.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
@@ -63,6 +61,8 @@
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/Triple.h"
 
 #include <sstream>
 
@@ -194,7 +194,7 @@
 
   // Ensure MCAsmInfo initialization occurs before any use, otherwise sections
   // may be created with a combination of default and explicit settings.
-  MAI->setCompressDebugSections(CompressDebugSections);
+  MCOptions.CompressDebugSections = CompressDebugSections;
 
   // Build up the feature string from the target feature list.
   std::string FS;
@@ -242,8 +242,7 @@
     Out = BOS.get();
   }
 
-  std::unique_ptr<MCCodeEmitter> CE(
-      TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+  std::unique_ptr<MCCodeEmitter> CE(TheTarget->createMCCodeEmitter(*MCII, Ctx));
   std::unique_ptr<MCAsmBackend> MAB(
       TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
   std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(*Out);
diff --git a/driver/LinuxToolChain.cpp b/driver/LinuxToolChain.cpp
index a12b794..f8ebea7 100644
--- a/driver/LinuxToolChain.cpp
+++ b/driver/LinuxToolChain.cpp
@@ -101,7 +101,7 @@
 Tool *Linux::buildCompiler()
 {
   llvm::opt::Arg *xarg = driver().args().getLastArg(gollvm::options::OPT_x);
-  if (xarg != nullptr && llvm::StringRef(xarg->getValue()).equals("c")) {
+  if (xarg != nullptr && xarg->containsValue("c")) {
     // This is a dummy C compile.
     return new DummyCompileC(*this, driver().executablePath());
   }
diff --git a/driver/Tool.h b/driver/Tool.h
index abde3ca..17d3b19 100644
--- a/driver/Tool.h
+++ b/driver/Tool.h
@@ -13,9 +13,9 @@
 #ifndef GOLLVM_DRIVER_TOOL_H
 #define GOLLVM_DRIVER_TOOL_H
 
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Triple.h"
 #include "Artifact.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/TargetParser/Triple.h"
 
 namespace gollvm {
 namespace driver {
diff --git a/driver/ToolChain.h b/driver/ToolChain.h
index c20e14b..89a28df 100644
--- a/driver/ToolChain.h
+++ b/driver/ToolChain.h
@@ -13,9 +13,9 @@
 #ifndef GOLLVM_DRIVER_TOOLCHAIN_H
 #define GOLLVM_DRIVER_TOOLCHAIN_H
 
-#include <string>
-#include "llvm/ADT/Triple.h"
 #include "llvm/Option/ArgList.h"
+#include "llvm/TargetParser/Triple.h"
+#include <string>
 
 #include "Action.h"
 #include "Tool.h"
diff --git a/libgo/godumpspec/godumpspec.cpp b/libgo/godumpspec/godumpspec.cpp
index 37a854d..15a66d2 100644
--- a/libgo/godumpspec/godumpspec.cpp
+++ b/libgo/godumpspec/godumpspec.cpp
@@ -26,6 +26,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/ObjectFile.h"
diff --git a/passes/GC.cpp b/passes/GC.cpp
index 3932bab..e5dd0bc 100644
--- a/passes/GC.cpp
+++ b/passes/GC.cpp
@@ -44,7 +44,7 @@
     // TODO: write barrier?
   }
 
-  Optional<bool> isGCManagedPointer(const Type *Ty) const override {
+  std::optional<bool> isGCManagedPointer(const Type *Ty) const override {
     return isa<PointerType>(Ty);
   }
 };
@@ -171,7 +171,7 @@
       for (uint8_t Byte : V)
         OS.emitIntValue(Byte, 1);
     }
-    OS.emitValueToAlignment(8);
+    OS.emitValueToAlignment(llvm::Align(8));
   }
 }
 
@@ -183,7 +183,7 @@
   // Create the section.
   MCSection *StackMapSection =
       OutContext.getObjectFileInfo()->getStackMapSection();
-  OS.SwitchSection(StackMapSection);
+  OS.switchSection(StackMapSection);
 
   emitCallsiteEntries(SM, OS);
 
diff --git a/passes/GoAnnotation.cpp b/passes/GoAnnotation.cpp
index 95f5fe3..d180248 100644
--- a/passes/GoAnnotation.cpp
+++ b/passes/GoAnnotation.cpp
@@ -19,6 +19,7 @@
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/IR/Module.h"
 #include "llvm/PassRegistry.h"
 #include "llvm/Target/TargetMachine.h"
 
diff --git a/passes/GoNilChecks.cpp b/passes/GoNilChecks.cpp
index 7561d65..e091699 100644
--- a/passes/GoNilChecks.cpp
+++ b/passes/GoNilChecks.cpp
@@ -25,8 +25,6 @@
 #include "GollvmPasses.h"
 
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
@@ -102,11 +100,11 @@
 
     /// If non-None, then an instruction in \p Insts that also must be
     /// hoisted.
-    Optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence;
+    std::optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence;
 
     /*implicit*/ DependenceResult(
         bool CanReorder,
-        Optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence)
+        std::optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence)
         : CanReorder(CanReorder), PotentialDependence(PotentialDependence) {
       assert((!PotentialDependence || CanReorder) &&
              "!CanReorder && PotentialDependence.hasValue() not allowed!");
@@ -252,18 +250,18 @@
   assert(llvm::all_of(Block, canHandle) && "Check this first!");
   assert(!is_contained(Block, MI) && "Block must be exclusive of MI!");
 
-  Optional<ArrayRef<MachineInstr *>::iterator> Dep;
+  std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
 
   for (auto I = Block.begin(), E = Block.end(); I != E; ++I) {
     if (canReorder(*I, MI))
       continue;
 
-    if (Dep == None) {
+    if (!Dep.has_value()) {
       // Found one possible dependency, keep track of it.
       Dep = I;
     } else {
       // We found two dependencies, so bail out.
-      return {false, None};
+      return {false, {}};
     }
   }
 
@@ -654,7 +652,7 @@
     // Insert an *unconditional* branch to not-null successor.
     if (!CheckBB->isLayoutSuccessor(NC.getNotNullSucc()))
       TII->insertBranch(*CheckBB, NC.getNotNullSucc(), nullptr,
-                        /*Cond=*/None, DL);
+                        /*Cond=*/{}, DL);
 
     NumImplicitNullChecks++;
 
@@ -724,7 +722,7 @@
         MI.eraseFromParent();
         break;
       }
-    TII->insertBranch(*FaultBB, PadBB, nullptr, None, DL);
+    TII->insertBranch(*FaultBB, PadBB, nullptr, {}, DL);
   }
 
   // Add EH labels before and after the faulting op.
diff --git a/passes/GoSafeGetg.cpp b/passes/GoSafeGetg.cpp
index 52b54c2..66dbedd 100644
--- a/passes/GoSafeGetg.cpp
+++ b/passes/GoSafeGetg.cpp
@@ -12,6 +12,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "GoSafeGetg.h"
 #include "GollvmPasses.h"
 
 #include "llvm/IR/BasicBlock.h"
@@ -76,8 +77,7 @@
 // found, replace it with a call of runtime.getg (i.e. undoing the
 // inlining).
 //
-bool
-GoSafeGetg::runOnModule(Module &M) {
+static bool updateModule(Module &M) {
   GlobalVariable *GV = M.getGlobalVariable("runtime.g");
   if (!GV)
     return false; // no access of g, nothing to do
@@ -132,3 +132,12 @@
 
   return Changed;
 }
+
+bool GoSafeGetg::runOnModule(Module &M) { return updateModule(M); }
+
+PreservedAnalyses GoSafeGetgPass::run(Module &M, ModuleAnalysisManager &AM) {
+  bool Changed = updateModule(M);
+  if (!Changed)
+    return PreservedAnalyses::all();
+  return PreservedAnalyses::none();
+}
diff --git a/passes/GoSafeGetg.h b/passes/GoSafeGetg.h
new file mode 100644
index 0000000..89e9d23
--- /dev/null
+++ b/passes/GoSafeGetg.h
@@ -0,0 +1,26 @@
+//===--- GoSafeGetg.h -----------------------------------------------------===//
+//
+// Copyright 2024 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// Make sure the TLS address is not cached across a thread switch.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_GOLLVM_PASSES_GOSAFEGETG_H
+#define LLVM_GOLLVM_PASSES_GOSAFEGETG_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct GoSafeGetgPass : public PassInfoMixin<GoSafeGetgPass> {
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_GOLLVM_PASSES_GOSAFEGETG_H
diff --git a/passes/GoStatepoints.cpp b/passes/GoStatepoints.cpp
index 6453db4..02062f6 100644
--- a/passes/GoStatepoints.cpp
+++ b/passes/GoStatepoints.cpp
@@ -22,8 +22,6 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallSet.h"
@@ -34,6 +32,7 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/IR/Argument.h"
+#include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
@@ -58,8 +57,8 @@
 #include "llvm/IR/User.h"
 #include "llvm/IR/Value.h"
 #include "llvm/IR/ValueHandle.h"
-#include "llvm/Pass.h"
 #include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compiler.h"
@@ -120,6 +119,32 @@
 
 static bool shouldRewriteStatepointsIn(Function &F);
 
+static Type *getPointerElementType(Value *V) {
+  assert(V->getType()->isPointerTy());
+  if (Argument *A = dyn_cast<Argument>(V)) {
+    if (A->hasByValAttr())
+      return A->getParamByValType();
+    // FIXME: Do we need to handle other types of arguments?
+    // fallback to ptr
+    return V->getType();
+
+  } else if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
+    return AI->getAllocatedType();
+  } else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V)) {
+    return GEP->getResultElementType();
+  } else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(V)) {
+    auto *Inst = CE->getAsInstruction();
+    Type *ITy = getPointerElementType(CE->getAsInstruction());
+    Inst->deleteValue();
+    return ITy;
+  } else if (GlobalValue *GV = dyn_cast<GlobalValue>(V)) {
+    return GV->getValueType();
+  } else {
+    // fallback to ptr
+    return V->getType();
+  }
+}
+
 PreservedAnalyses GoStatepoints::run(Module &M,
                                      ModuleAnalysisManager &AM) {
   // Create a sentinel global variable for stack maps.
@@ -993,8 +1018,9 @@
       // TODO: In many cases, the new instruction is just EE itself.  We should
       // exploit this, but can't do it here since it would break the invariant
       // about the BDV not being known to be a base.
-      auto *BaseInst = ExtractElementInst::Create(
-          State.getBaseValue(), EE->getIndexOperand(), "base_ee", EE);
+      auto *BaseInst = ExtractElementInst::Create(State.getBaseValue(),
+                                                  EE->getIndexOperand(),
+                                                  "base_ee", EE->getIterator());
       BaseInst->setMetadata("is_base_value", MDNode::get(I->getContext(), {}));
       States[I] = BDVState(BDVState::Base, BaseInst);
     }
@@ -1015,29 +1041,30 @@
         int NumPreds = pred_size(BB);
         assert(NumPreds > 0 && "how did we reach here");
         std::string Name = suffixed_name_or(I, ".base", "base_phi");
-        return PHINode::Create(I->getType(), NumPreds, Name, I);
+        return PHINode::Create(I->getType(), NumPreds, Name, I->getIterator());
       } else if (SelectInst *SI = dyn_cast<SelectInst>(I)) {
         // The undef will be replaced later
         UndefValue *Undef = UndefValue::get(SI->getType());
         std::string Name = suffixed_name_or(I, ".base", "base_select");
-        return SelectInst::Create(SI->getCondition(), Undef, Undef, Name, SI);
+        return SelectInst::Create(SI->getCondition(), Undef, Undef, Name,
+                                  SI->getIterator());
       } else if (auto *EE = dyn_cast<ExtractElementInst>(I)) {
         UndefValue *Undef = UndefValue::get(EE->getVectorOperand()->getType());
         std::string Name = suffixed_name_or(I, ".base", "base_ee");
         return ExtractElementInst::Create(Undef, EE->getIndexOperand(), Name,
-                                          EE);
+                                          EE->getIterator());
       } else if (auto *IE = dyn_cast<InsertElementInst>(I)) {
         UndefValue *VecUndef = UndefValue::get(IE->getOperand(0)->getType());
         UndefValue *ScalarUndef = UndefValue::get(IE->getOperand(1)->getType());
         std::string Name = suffixed_name_or(I, ".base", "base_ie");
-        return InsertElementInst::Create(VecUndef, ScalarUndef,
-                                         IE->getOperand(2), Name, IE);
+        return InsertElementInst::Create(
+            VecUndef, ScalarUndef, IE->getOperand(2), Name, IE->getIterator());
       } else {
         auto *SV = cast<ShuffleVectorInst>(I);
         UndefValue *VecUndef = UndefValue::get(SV->getOperand(0)->getType());
         std::string Name = suffixed_name_or(I, ".base", "base_sv");
         return new ShuffleVectorInst(VecUndef, VecUndef, SV->getOperand(2),
-                                     Name, SV);
+                                     Name, SV->getIterator());
       }
     };
     Instruction *BaseInst = MakeBaseInstPlaceholder(I);
@@ -1067,7 +1094,8 @@
     assert(Base && "Can't be null");
     // The cast is needed since base traversal may strip away bitcasts
     if (Base->getType() != Input->getType() && InsertPt)
-      Base = CastInst::CreatePointerBitCastOrAddrSpaceCast(Base, Input->getType(), "cast", InsertPt);
+      Base = CastInst::CreatePointerBitCastOrAddrSpaceCast(
+          Base, Input->getType(), "cast", InsertPt->getIterator());
     return Base;
   };
 
@@ -1262,8 +1290,6 @@
 // both function declarations and call sites.
 static constexpr Attribute::AttrKind FnAttrsToStrip[] =
   {Attribute::ReadNone, Attribute::ReadOnly, Attribute::WriteOnly,
-   Attribute::ArgMemOnly, Attribute::InaccessibleMemOnly,
-   Attribute::InaccessibleMemOrArgMemOnly,
    Attribute::NoSync, Attribute::NoFree};
 
 // List of all parameter and return attributes which must be stripped when
@@ -1356,7 +1382,7 @@
       // Note: we've inserted instructions, so the call to llvm.deoptimize may
       // not necessarily be followed by the matching return.
       auto *RI = cast<ReturnInst>(OldI->getParent()->getTerminator());
-      new UnreachableInst(RI->getContext(), RI);
+      new UnreachableInst(RI->getContext(), RI->getIterator());
       RI->eraseFromParent();
     }
 
@@ -1430,7 +1456,7 @@
     if (isa<AllocaInst>(V) ||
         (isa<Argument>(V) && cast<Argument>(V)->hasByValAttr())) {
       // Byval argument is at a fixed frame offset. Treat it the same as alloca.
-      Type *T = cast<PointerType>(V->getType())->getElementType();
+      Type *T = getPointerElementType(V);
       if (hasPointer(T)) {
         PtrFields.push_back(V);
         getPtrBitmapForType(T, DL, PtrFields);
@@ -1627,8 +1653,9 @@
   SetVector<Value *> AllAllocas;
   if (ClobberNonLive)
     for (Instruction &I : F.getEntryBlock())
-      if (isa<AllocaInst>(I) && hasPointer(I.getType()->getPointerElementType()))
-        AllAllocas.insert(&I);
+      if (AllocaInst *AI = dyn_cast<AllocaInst>(&I))
+        if (hasPointer(AI->getAllocatedType()))
+          AllAllocas.insert(&I);
 
   computeLiveInValues(DT, F, OriginalLivenessData, AddrTakenAllocas,
                       ToZero, BadLoads, DVCache);
@@ -1702,7 +1729,7 @@
           } else if (ToZero.count(V) != 0) {
             // Non-addrtaken alloca. Just insert zeroing, keep the lifetime marker.
             IRBuilder<> Builder(I.getNextNode());
-            Value *Zero = Constant::getNullValue(V->getType()->getPointerElementType());
+            Value *Zero = Constant::getNullValue(getPointerElementType(V));
             Builder.CreateStore(Zero, V);
             // Don't remove V from ToZero for now, as there may be multiple
             // lifetime start markers, where we need to insert zeroing.
@@ -1737,14 +1764,14 @@
   for (Instruction &I : F.getEntryBlock())
     if (ToZero.count(&I) != 0) {
       IRBuilder<> Builder(I.getNextNode());
-      Type *ElemTyp = I.getType()->getPointerElementType();
       if (AddrTakenAllocas.count(&I) != 0) {
+        Type *ElemTyp = cast<AllocaInst>(I).getAllocatedType();
         // For addrtaken alloca, we removed the lifetime marker above.
         // Insert a new one at the entry block.
         unsigned Size = DL.getTypeStoreSize(ElemTyp);
         Builder.CreateLifetimeStart(&I, ConstantInt::get(Int64Ty, Size));
       }
-      Value *Zero = Constant::getNullValue(ElemTyp);
+      Value *Zero = Constant::getNullValue(getPointerElementType(&I));
       Builder.CreateStore(Zero, &I);
       ToZero.remove(&I);
     }
@@ -1942,8 +1969,8 @@
     // That Value* no longer exists and we need to use the new gc_result.
     // Thankfully, the live set is embedded in the statepoint (and updated), so
     // we just grab that.
-    Live.insert(Live.end(), Info.StatepointToken->gc_args_begin(),
-                Info.StatepointToken->gc_args_end());
+    Live.insert(Live.end(), Info.StatepointToken->gc_live_begin(),
+                Info.StatepointToken->gc_live_end());
 #ifndef NDEBUG
     // Do some basic sanity checks on our liveness results before performing
     // relocation.  Relocation can and will turn mistakes in liveness results
@@ -1951,7 +1978,7 @@
     // TODO: It would be nice to test consistency as well
     assert(DT.isReachableFromEntry(Info.StatepointToken->getParent()) &&
            "statepoint must be reachable or liveness is meaningless");
-    for (Value *V : Info.StatepointToken->gc_args()) {
+    for (Value *V : Info.StatepointToken->gc_live()) {
       if (!isa<Instruction>(V))
         // Non-instruction values trivial dominate all possible uses
         continue;
@@ -2024,7 +2051,7 @@
   if (isa<PointerType>(F.getReturnType()))
     RemoveNonValidAttrAtIndex(Ctx, F, AttributeList::ReturnIndex);
 
-    for (auto Attr : FnAttrsToStrip)
+  for (auto Attr : FnAttrsToStrip)
     F.removeFnAttr(Attr);
 }
 
@@ -2161,11 +2188,12 @@
       // Create a dummy landing pad block.
       LLVMContext &C = F.getContext();
       BasicBlock *PadBB = BasicBlock::Create(C, "dummy", &F);
-      Type *ExnTy = StructType::get(Type::getInt8PtrTy(C), Type::getInt32Ty(C));
+      Type *ExnTy =
+          StructType::get(PointerType::getUnqual(C), Type::getInt32Ty(C));
 
       LandingPadInst *LPad =
           LandingPadInst::Create(ExnTy, 1, "dummy.ex", PadBB);
-      LPad->addClause(Constant::getNullValue(Type::getInt8PtrTy(C)));
+      LPad->addClause(Constant::getNullValue(PointerType::getUnqual(C)));
       new UnreachableInst(PadBB->getContext(), PadBB);
 
       BasicBlock *Old = CI->getParent();
@@ -2179,7 +2207,7 @@
       for (DomTreeNode *I : Children)
         DT.changeImmediateDominator(I, NewNode);
 
-      DTU.insertEdge(Old, PadBB);
+      DTU.applyUpdates({{DominatorTree::Insert, Old, PadBB}});
     }
   }
 
@@ -2269,10 +2297,11 @@
 
 static Value*
 isTrackedAlloca(Value *V, DefiningValueMapTy &DVCache) {
-  Value *Base = isAlloca(V, DVCache);
-  if (Base &&
-      hasPointer(Base->getType()->getPointerElementType()))
-    return Base;
+  if (Value *Base = isAlloca(V, DVCache)) {
+    Type *AllocedTy = cast<AllocaInst>(Base)->getAllocatedType();
+    if (Base && hasPointer(AllocedTy))
+      return Base;
+  }
   return nullptr;
 }
 
@@ -2450,7 +2479,7 @@
   // Use the metadata inserted by the FE.
   for (Instruction &I : F.getEntryBlock())
     if (isa<AllocaInst>(I) && I.getMetadata("go_addrtaken") &&
-        hasPointer(I.getType()->getPointerElementType()))
+        hasPointer(cast<AllocaInst>(I).getAllocatedType()))
       AddrTakenAllocas.insert(&I);
 
   // The FE's addrtaken mark may be imprecise. Look for certain
@@ -2581,7 +2610,7 @@
                SetVector<Value *> &ToZero,
                DefiningValueMapTy &DVCache) {
   unsigned PtrSize = DL.getPointerSize();
-  unsigned Size = DL.getTypeStoreSize(V->getType()->getPointerElementType());
+  unsigned Size = DL.getTypeStoreSize(cast<AllocaInst>(V)->getAllocatedType());
   if (Size <= PtrSize)
     return;
 
@@ -2597,7 +2626,8 @@
       if (hasStructRetAttr(CI)) {
         Value *Ptr = CI->getOperand(0);
         if (isTrackedAlloca(Ptr, DVCache) == V)
-          StoreSize += DL.getTypeStoreSize(Ptr->getType()->getPointerElementType());
+          StoreSize +=
+              DL.getTypeStoreSize(cast<AllocaInst>(Ptr)->getAllocatedType());
       }
       if (Function *Fn = CI->getCalledFunction())
         switch (Fn->getIntrinsicID()) {
@@ -2621,7 +2651,9 @@
       if (hasStructRetAttr(II)) {
         Value *Ptr = II->getOperand(0);
         if (isTrackedAlloca(Ptr, DVCache) == V)
-          if (DL.getTypeStoreSize(Ptr->getType()->getPointerElementType()) + PtrSize - 1 >= Size)
+          if (DL.getTypeStoreSize(cast<AllocaInst>(Ptr)->getAllocatedType()) +
+                  PtrSize - 1 >=
+              Size)
             // We are storing the whole type
             return;
 
@@ -2919,8 +2951,8 @@
       Value *V = Ptr->stripPointerCasts();
       const DataLayout &DL = Inst->getModule()->getDataLayout();
       if (!Data.LiveIn[BB].count(V) &&
-          (DL.getTypeStoreSize(Ptr->getType()->getPointerElementType()) >=
-           DL.getTypeStoreSize(V->getType()->getPointerElementType())))
+          (DL.getTypeStoreSize(getPointerElementType(Ptr)) >=
+           DL.getTypeStoreSize(getPointerElementType(V))))
         LiveOut.remove(V);
     }
 
@@ -2935,7 +2967,7 @@
       Value *Bad = ConstantInt::get(Int8Ty, 0xff);
       for (Value *Alloca : ToClobber) {
         unsigned Siz =
-            DL.getTypeStoreSize(Alloca->getType()->getPointerElementType());
+            DL.getTypeStoreSize(cast<AllocaInst>(Alloca)->getAllocatedType());
         Builder.CreateMemSet(Alloca, Bad, Siz, MaybeAlign(0));
         //dbgs() << "clobber " << *Alloca << " at " << *Inst << "\n";
       }
@@ -2962,7 +2994,7 @@
   for (Instruction &I : instructions(F)) {
     if (auto *CI = dyn_cast<CallInst>(&I))
       if (Function *Callee = CI->getCalledFunction()) {
-        if (Callee->getName().equals("runtime.gcWriteBarrier")) {
+        if (Callee->getName() == "runtime.gcWriteBarrier") {
           // gcWriteBarrier(dst, val)
           // there is an extra "nest" argument.
           Value *Dst = CI->getArgOperand(1), *Val = CI->getArgOperand(2);
@@ -2974,7 +3006,7 @@
                                       PointerType::get(Val->getType(), AS));
           Builder.CreateStore(Val, Dst);
           ToDel.insert(CI);
-        } else if (Callee->getName().equals("runtime.typedmemmove")) {
+        } else if (Callee->getName() == "runtime.typedmemmove") {
           // typedmemmove(typ, dst, src)
           // there is an extra "nest" argument.
           Value *Dst = CI->getArgOperand(2), *Src = CI->getArgOperand(3);
@@ -2986,7 +3018,7 @@
           // for now. The size is the first field. The optimizer should be
           // able to constant-fold it.
           Value *TD = CI->getArgOperand(1);
-          Type *etyp = TD->getType()->getPointerElementType();
+          Type *etyp = cast<GlobalValue>(TD)->getValueType();
           Value *GEP = Builder.CreateConstInBoundsGEP2_32(
               etyp, TD, 0, 0);
           Value *Siz = Builder.CreateLoad(etyp, GEP);
diff --git a/passes/GollvmPasses.h b/passes/GollvmPasses.h
index 398df82..d1900a4 100644
--- a/passes/GollvmPasses.h
+++ b/passes/GollvmPasses.h
@@ -11,6 +11,10 @@
 
 #include "llvm/ADT/SmallVector.h"
 
+#include "GoSafeGetg.h"
+#include "GoStatepoints.h"
+#include "RemoveAddrSpace.h"
+
 namespace llvm {
 
 class DataLayout;
@@ -25,14 +29,14 @@
 void initializeGoSafeGetgPass(PassRegistry&);
 void initializeGoStatepointsLegacyPassPass(PassRegistry&);
 void initializeGoWrappersPass(PassRegistry&);
-void initializeRemoveAddrSpacePassPass(PassRegistry&);
+void initializeRemoveAddrSpaceWrapperPass(PassRegistry&);
 
 FunctionPass *createGoAnnotationPass();
 FunctionPass *createGoNilChecksPass();
 ModulePass *createGoSafeGetgPass();
 ModulePass *createGoStatepointsLegacyPass();
 FunctionPass *createGoWrappersPass();
-ModulePass *createRemoveAddrSpacePass(const DataLayout&);
+ModulePass *createRemoveAddrSpaceWrapper(const DataLayout&);
 
 void linkGoGC();
 void linkGoGCPrinter();
diff --git a/passes/RemoveAddrSpace.cpp b/passes/RemoveAddrSpace.cpp
index e31e9ac..085db3d 100644
--- a/passes/RemoveAddrSpace.cpp
+++ b/passes/RemoveAddrSpace.cpp
@@ -1,4 +1,4 @@
-//===--- GoStackMap.cpp ---------------------------------------------------===//
+//===--- RemoveAddrSpace.cpp ----------------------------------------------===//
 //
 // Copyright 2018 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -12,6 +12,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "RemoveAddrSpace.h"
 #include "GollvmPasses.h"
 
 #include "llvm/ADT/DenseSet.h"
@@ -27,21 +28,8 @@
 
 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
+class RemoveAddrSpace {
+  Type *IntTy {nullptr}; // 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
 
@@ -49,20 +37,44 @@
   // of ptr-to-int and int-to-ptr casts, as codegen cannot handle
   // addrspacecast in static initializer.
   void removeAddrSpaceCast(Constant *C);
+
+public:
+  RemoveAddrSpace(const DataLayout &DL) : DL(DL) {}
+  bool runOnModule(Module &M);
+};
+
+
+class RemoveAddrSpaceWrapper : public ModulePass {
+  DataLayout DL;
+
+ public:
+  static char ID;
+
+  RemoveAddrSpaceWrapper() : ModulePass(ID), DL("") {}
+
+  RemoveAddrSpaceWrapper(const DataLayout &DL) : ModulePass(ID), DL(DL) {
+    initializeRemoveAddrSpaceWrapperPass(
+        *PassRegistry::getPassRegistry());
+  }
+
+  bool runOnModule(Module &M) override {
+    RemoveAddrSpace R(DL);
+    return R.runOnModule(M);
+  }
 };
 
 } // namespace
 
-char RemoveAddrSpacePass::ID = 0;
-INITIALIZE_PASS(RemoveAddrSpacePass, "remove-addrspacecast",
+char RemoveAddrSpaceWrapper::ID = 0;
+INITIALIZE_PASS(RemoveAddrSpaceWrapper, "remove-addrspacecast",
                 "Remove addrspacecast instructions", false,
                 false)
-ModulePass *llvm::createRemoveAddrSpacePass(const DataLayout &DL) {
-  return new RemoveAddrSpacePass(DL);
+ModulePass *llvm::createRemoveAddrSpaceWrapper(const DataLayout &DL) {
+  return new RemoveAddrSpaceWrapper(DL);
 }
 
 void
-RemoveAddrSpacePass::removeAddrSpaceCast(Constant *C) {
+RemoveAddrSpace::removeAddrSpaceCast(Constant *C) {
   if (Visited.count(C))
     return;
   Visited.insert(C);
@@ -86,7 +98,7 @@
 }
 
 bool
-RemoveAddrSpacePass::runOnModule(Module &M) {
+RemoveAddrSpace::runOnModule(Module &M) {
   // At this point we no longer need non-integral pointers.
   // Set data layout back to default.
   M.setDataLayout(DL);
@@ -97,3 +109,10 @@
       removeAddrSpaceCast(GV.getInitializer());
   return true;
 }
+
+PreservedAnalyses llvm::RemoveAddrSpacePass::run(Module &M,
+                                                 ModuleAnalysisManager &AM) {
+  RemoveAddrSpace R(DL);
+  R.runOnModule(M);
+  return PreservedAnalyses::all();
+}
diff --git a/passes/RemoveAddrSpace.h b/passes/RemoveAddrSpace.h
new file mode 100644
index 0000000..e994139
--- /dev/null
+++ b/passes/RemoveAddrSpace.h
@@ -0,0 +1,36 @@
+//===--- RemoveAddrSpace.h ------------------------------------------------===//
+//
+// 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).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_GOLLVM_PASSES_REMOVEADDRSPACE_H
+#define LLVM_GOLLVM_PASSES_REMOVEADDRSPACE_H
+
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class RemoveAddrSpacePass : public PassInfoMixin<RemoveAddrSpacePass> {
+  DataLayout DL; // data layout without non-integral pointer
+
+public:
+  /// Construct a pass with update data layout optional optmizations.
+  RemoveAddrSpacePass(const DataLayout &DL) : DL(DL) {}
+
+  /// Run the pass over the module.
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_GOLLVM_PASSES_REMOVEADDRSPACE_H
diff --git a/passes/Util.cpp b/passes/Util.cpp
index 08b3ddb..00bc3bd 100644
--- a/passes/Util.cpp
+++ b/passes/Util.cpp
@@ -61,7 +61,7 @@
     for (unsigned i = 0, n = T->getArrayNumElements(); i < n; ++i) {
       Value *ivals[2] = { ConstantInt::get(Int32Ty, 0),
                           ConstantInt::get(Int32Ty, i) };
-      ArrayRef<Value*> Idx = makeArrayRef(ivals, 2);
+      ArrayRef<Value *> Idx(ivals, 2);
       uint64_t Offset = DL.getIndexedOffsetInType(T, Idx);
       getPtrBitmapForTypeHelper(ET, DL, BaseOffset+Offset, BV);
     }
@@ -73,7 +73,7 @@
     for (unsigned i = 0, n = VT->getNumElements(); i < n; ++i) {
       Value *ivals[2] = { ConstantInt::get(Int32Ty, 0),
                           ConstantInt::get(Int32Ty, i) };
-      ArrayRef<Value*> Idx = makeArrayRef(ivals, 2);
+      ArrayRef<Value *> Idx(ivals, 2);
       uint64_t Offset = DL.getIndexedOffsetInType(T, Idx);
       getPtrBitmapForTypeHelper(ET, DL, BaseOffset+Offset, BV);
     }
@@ -86,7 +86,7 @@
         continue;
       Value *ivals[2] = { ConstantInt::get(Int32Ty, 0),
                           ConstantInt::get(Int32Ty, i) };
-      ArrayRef<Value*> Idx = makeArrayRef(ivals, 2);
+      ArrayRef<Value *> Idx(ivals, 2);
       uint64_t Offset = DL.getIndexedOffsetInType(T, Idx);
       getPtrBitmapForTypeHelper(ET, DL, BaseOffset+Offset, BV);
     }
diff --git a/unittests/BackendCore/BackendArrayStruct.cpp b/unittests/BackendCore/BackendArrayStruct.cpp
index a488338..4fd7244 100644
--- a/unittests/BackendCore/BackendArrayStruct.cpp
+++ b/unittests/BackendCore/BackendArrayStruct.cpp
@@ -79,19 +79,18 @@
   h.mkAssign(bfex2, bc2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { i8*, i32 }* %loc1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i8*, i32 }* @const.0 to i8*), i64 16, i1 false)
-    store { i8*, i32 }* %loc1, { i8*, i32 }** %loc2, align 8
-    store i32 0, i32* %x, align 4
-    %field.0 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %loc1, i32 0, i32 1
-    %loc1.field.ld.0 = load i32, i32* %field.0, align 4
-    store i32 %loc1.field.ld.0, i32* %x, align 4
-    store i8 0, i8* %b2, align 1
-    %field.1 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %loc1, i32 0, i32 0
-    store i8* %b2, i8** %field.1, align 8
-    %loc2.ld.0 = load { i8*, i32 }*, { i8*, i32 }** %loc2, align 8
-    %field.2 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %loc2.ld.0, i32 0, i32 1
-    store i32 2, i32* %field.2, align 4
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %loc1, ptr align 8 @const.0, i64 16, i1 false)
+    store ptr %loc1, ptr %loc2, align 8
+    store i32 0, ptr %x, align 4
+    %field.0 = getelementptr inbounds { ptr, i32 }, ptr %loc1, i32 0, i32 1
+    %loc1.field.ld.0 = load i32, ptr %field.0, align 4
+    store i32 %loc1.field.ld.0, ptr %x, align 4
+    store i8 0, ptr %b2, align 1
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %loc1, i32 0, i32 0
+    store ptr %b2, ptr %field.1, align 8
+    %loc2.ld.0 = load ptr, ptr %loc2, align 8
+    %field.2 = getelementptr inbounds { ptr, i32 }, ptr %loc2.ld.0, i32 0, i32 1
+    store i32 2, ptr %field.2, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -145,24 +144,24 @@
   h.mkAssign(vex2, fex2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
-    %tmp.0 = alloca { i8*, i32 }, align 8
+    %tmp.0 = alloca { ptr, i32 }, align 8
     %x = alloca i32, align 4
     %y = alloca i32, align 4
     %z = alloca i32, align 4
-    store i32 0, i32* %x, align 4
-    store i32 0, i32* %y, align 4
-    %y.ld.0 = load i32, i32* %y, align 4
-    %field.0 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %tmp.0, i32 0, i32 0
-    store i8* null, i8** %field.0, align 8
-    %field.1 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %tmp.0, i32 0, i32 1
-    store i32 %y.ld.0, i32* %field.1, align 4
-    %field.2 = getelementptr inbounds { i8*, i32 }, { i8*, i32 }* %tmp.0, i32 0, i32 1
-    %.field.ld.0 = load i32, i32* %field.2, align 4
-    store i32 %.field.ld.0, i32* %x, align 4
-    store i32 0, i32* %z, align 4
-    store i32 42, i32* %z, align 4
+    store i32 0, ptr %x, align 4
+    store i32 0, ptr %y, align 4
+    %y.ld.0 = load i32, ptr %y, align 4
+    %field.0 = getelementptr inbounds { ptr, i32 }, ptr %tmp.0, i32 0, i32 0
+    store ptr null, ptr %field.0, align 8
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %tmp.0, i32 0, i32 1
+    store i32 %y.ld.0, ptr %field.1, align 4
+    %field.2 = getelementptr inbounds { ptr, i32 }, ptr %tmp.0, i32 0, i32 1
+    %.field.ld.0 = load i32, ptr %field.2, align 4
+    store i32 %.field.ld.0, ptr %x, align 4
+    store i32 0, ptr %z, align 4
+    store i32 42, ptr %z, align 4
     ret void
   }
   )RAW_RESULT");
@@ -231,34 +230,34 @@
   h.mkAssign(vex3, eex3);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
     %tmp.0 = alloca [4 x i64], align 8
     %x = alloca i64, align 8
     %y = alloca i64, align 8
     %z = alloca i64, align 8
     %w = alloca i64, align 8
-    store i64 0, i64* %x, align 8
-    store i64 0, i64* %y, align 8
-    %y.ld.0 = load i64, i64* %y, align 8
-    %index.0 = getelementptr [4 x i64], [4 x i64]* %tmp.0, i32 0, i32 0
-    store i64 %y.ld.0, i64* %index.0, align 8
-    %index.1 = getelementptr [4 x i64], [4 x i64]* %tmp.0, i32 0, i32 1
-    store i64 3, i64* %index.1, align 8
-    %index.2 = getelementptr [4 x i64], [4 x i64]* %tmp.0, i32 0, i32 2
-    store i64 2, i64* %index.2, align 8
-    %index.3 = getelementptr [4 x i64], [4 x i64]* %tmp.0, i32 0, i32 3
-    store i64 1, i64* %index.3, align 8
-    %index.4 = getelementptr [4 x i64], [4 x i64]* %tmp.0, i32 0, i32 1
-    %.index.ld.0 = load i64, i64* %index.4, align 8
-    store i64 %.index.ld.0, i64* %x, align 8
-    store i64 0, i64* %z, align 8
-    store i64 3, i64* %z, align 8
-    store i64 0, i64* %w, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
-    %index.5 = getelementptr [4 x i64], [4 x i64]* @const.0, i32 0, i64 %x.ld.0
-    %.index.ld.1 = load i64, i64* %index.5, align 8
-    store i64 %.index.ld.1, i64* %w, align 8
+    store i64 0, ptr %x, align 8
+    store i64 0, ptr %y, align 8
+    %y.ld.0 = load i64, ptr %y, align 8
+    %index.0 = getelementptr [4 x i64], ptr %tmp.0, i32 0, i32 0
+    store i64 %y.ld.0, ptr %index.0, align 8
+    %index.1 = getelementptr [4 x i64], ptr %tmp.0, i32 0, i32 1
+    store i64 3, ptr %index.1, align 8
+    %index.2 = getelementptr [4 x i64], ptr %tmp.0, i32 0, i32 2
+    store i64 2, ptr %index.2, align 8
+    %index.3 = getelementptr [4 x i64], ptr %tmp.0, i32 0, i32 3
+    store i64 1, ptr %index.3, align 8
+    %index.4 = getelementptr [4 x i64], ptr %tmp.0, i32 0, i32 1
+    %.index.ld.0 = load i64, ptr %index.4, align 8
+    store i64 %.index.ld.0, ptr %x, align 8
+    store i64 0, ptr %z, align 8
+    store i64 3, ptr %z, align 8
+    store i64 0, ptr %w, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
+    %index.5 = getelementptr [4 x i64], ptr @const.0, i32 0, i64 %x.ld.0
+    %.index.ld.1 = load i64, ptr %index.5, align 8
+    store i64 %.index.ld.1, ptr %w, align 8
     ret void
   }
   )RAW_RESULT");
@@ -306,20 +305,18 @@
   h.mkLocal("ac", at4, arcon3);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast [4 x i64]* %aa to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ([4 x i64]* @const.0 to i8*), i64 32, i1 false)
-    %cast.1 = bitcast [4 x i64]* %ab to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 bitcast ([4 x i64]* @const.1 to i8*), i64 32, i1 false)
-    store i64 0, i64* %z, align 8
-    %z.ld.0 = load i64, i64* %z, align 8
-    %index.0 = getelementptr [4 x i64], [4 x i64]* %ac, i32 0, i32 0
-    store i64 0, i64* %index.0, align 8
-    %index.1 = getelementptr [4 x i64], [4 x i64]* %ac, i32 0, i32 1
-    store i64 %z.ld.0, i64* %index.1, align 8
-    %index.2 = getelementptr [4 x i64], [4 x i64]* %ac, i32 0, i32 2
-    store i64 0, i64* %index.2, align 8
-    %index.3 = getelementptr [4 x i64], [4 x i64]* %ac, i32 0, i32 3
-    store i64 0, i64* %index.3, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %aa, ptr align 8 @const.0, i64 32, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %ab, ptr align 8 @const.1, i64 32, i1 false)
+    store i64 0, ptr %z, align 8
+    %z.ld.0 = load i64, ptr %z, align 8
+    %index.0 = getelementptr [4 x i64], ptr %ac, i32 0, i32 0
+    store i64 0, ptr %index.0, align 8
+    %index.1 = getelementptr [4 x i64], ptr %ac, i32 0, i32 1
+    store i64 %z.ld.0, ptr %index.1, align 8
+    %index.2 = getelementptr [4 x i64], ptr %ac, i32 0, i32 2
+    store i64 0, ptr %index.2, align 8
+    %index.3 = getelementptr [4 x i64], ptr %ac, i32 0, i32 3
+    store i64 0, ptr %index.3, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -367,14 +364,13 @@
   h.mkLocal("loc2", s2t, scon2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { i32*, i32 }* %loc1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i32*, i32 }* @const.0 to i8*), i64 16, i1 false)
-    %field.0 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %loc1, i32 0, i32 1
-    %loc1.field.ld.0 = load i32, i32* %field.0, align 4
-    %field.1 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %loc2, i32 0, i32 0
-    store i32* %param1.addr, i32** %field.1, align 8
-    %field.2 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %loc2, i32 0, i32 1
-    store i32 %loc1.field.ld.0, i32* %field.2, align 4
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %loc1, ptr align 8 @const.0, i64 16, i1 false)
+    %field.0 = getelementptr inbounds { ptr, i32 }, ptr %loc1, i32 0, i32 1
+    %loc1.field.ld.0 = load i32, ptr %field.0, align 4
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %loc2, i32 0, i32 0
+    store ptr %param1.addr, ptr %field.1, align 8
+    %field.2 = getelementptr inbounds { ptr, i32 }, ptr %loc2, i32 0, i32 1
+    store i32 %loc1.field.ld.0, ptr %field.2, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -423,18 +419,15 @@
   h.mkAssign(vex, scon2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { { i32*, i32 }, float }* %loc1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ { i32*, i32 }, float }* @const.0 to i8*), i64 24, i1 false)
-    %field.0 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %tmp.0, i32 0, i32 0
-    store i32* %param1.addr, i32** %field.0, align 8
-    %field.1 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %tmp.0, i32 0, i32 1
-    store i32 3, i32* %field.1, align 4
-    %field.2 = getelementptr inbounds { { i32*, i32 }, float }, { { i32*, i32 }, float }* %loc1, i32 0, i32 0
-    %cast.1 = bitcast { i32*, i32 }* %field.2 to i8*
-    %cast.2 = bitcast { i32*, i32 }* %tmp.0 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 %cast.2, i64 16, i1 false)
-    %field.3 = getelementptr inbounds { { i32*, i32 }, float }, { { i32*, i32 }, float }* %loc1, i32 0, i32 1
-    store float 3.000000e+00, float* %field.3, align 4
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %loc1, ptr align 8 @const.0, i64 24, i1 false)
+    %field.0 = getelementptr inbounds { ptr, i32 }, ptr %tmp.0, i32 0, i32 0
+    store ptr %param1.addr, ptr %field.0, align 8
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %tmp.0, i32 0, i32 1
+    store i32 3, ptr %field.1, align 4
+    %field.2 = getelementptr inbounds { { ptr, i32 }, float }, ptr %loc1, i32 0, i32 0
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %field.2, ptr align 8 %tmp.0, i64 16, i1 false)
+    %field.3 = getelementptr inbounds { { ptr, i32 }, float }, ptr %loc1, i32 0, i32 1
+    store float 3.000000e+00, ptr %field.3, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -472,12 +465,12 @@
   h.mkAssign(dex, scon);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %p0.ld.0 = load { i32*, i32 }*, { i32*, i32 }** %p0.addr, align 8
-    %p1.ld.0 = load i32*, i32** %p1.addr, align 8
-    %field.0 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %p0.ld.0, i32 0, i32 0
-    store i32* %p1.ld.0, i32** %field.0, align 8
-    %field.1 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %p0.ld.0, i32 0, i32 1
-    store i32 101, i32* %field.1, align 4
+    %p0.ld.0 = load ptr, ptr %p0.addr, align 8
+    %p1.ld.0 = load ptr, ptr %p1.addr, align 8
+    %field.0 = getelementptr inbounds { ptr, i32 }, ptr %p0.ld.0, i32 0, i32 0
+    store ptr %p1.ld.0, ptr %field.0, align 8
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %p0.ld.0, i32 0, i32 1
+    store i32 101, ptr %field.1, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -521,11 +514,11 @@
   h.mkLocal("t2", s1t, scon2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %x.ld.0 = load i32, i32* @x, align 4
-    store i32 %x.ld.0, i32* getelementptr inbounds ({ i32 }, { i32 }* @t, i32 0, i32 0), align 4
-    %t.field.ld.0 = load i32, i32* getelementptr inbounds ({ i32 }, { i32 }* @t, i32 0, i32 0), align 4
-    %field.2 = getelementptr inbounds { i32 }, { i32 }* %t2, i32 0, i32 0
-    store i32 %t.field.ld.0, i32* %field.2, align 4
+    %x.ld.0 = load i32, ptr @x, align 4
+    store i32 %x.ld.0, ptr @t, align 4
+    %t.field.ld.0 = load i32, ptr @t, align 4
+    %field.2 = getelementptr inbounds { i32 }, ptr %t2, i32 0, i32 0
+    store i32 %t.field.ld.0, ptr %field.2, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -575,16 +568,15 @@
   h.mkAssign(aa4, aa3);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast [4 x i64]* %aa to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ([4 x i64]* @const.0 to i8*), i64 32, i1 false)
-    %index.0 = getelementptr [4 x i64], [4 x i64]* %aa, i32 0, i32 1
-    %aa.index.ld.0 = load i64, i64* %index.0, align 8
-    %index.1 = getelementptr [4 x i64], [4 x i64]* %aa, i32 0, i64 %aa.index.ld.0
-    %index.2 = getelementptr [4 x i64], [4 x i64]* %aa, i32 0, i64 3
-    %aa.index.ld.1 = load i64, i64* %index.2, align 8
-    %index.3 = getelementptr [4 x i64], [4 x i64]* %aa, i32 0, i64 %aa.index.ld.1
-    %aa.index.ld.2 = load i64, i64* %index.3, align 8
-    store i64 %aa.index.ld.2, i64* %index.1, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %aa, ptr align 8 @const.0, i64 32, i1 false)
+    %index.0 = getelementptr [4 x i64], ptr %aa, i32 0, i32 1
+    %aa.index.ld.0 = load i64, ptr %index.0, align 8
+    %index.1 = getelementptr [4 x i64], ptr %aa, i32 0, i64 %aa.index.ld.0
+    %index.2 = getelementptr [4 x i64], ptr %aa, i32 0, i64 3
+    %aa.index.ld.1 = load i64, ptr %index.2, align 8
+    %index.3 = getelementptr [4 x i64], ptr %aa, i32 0, i64 %aa.index.ld.1
+    %aa.index.ld.2 = load i64, ptr %index.3, align 8
+    store i64 %aa.index.ld.2, ptr %index.1, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -644,15 +636,12 @@
     h.mkAssign(fx, bi64five);
 
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast [10 x { i8, [4 x { i64, i64 }*], i8 }*]* %t1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ([10 x { i8, [4 x { i64, i64 }*], i8 }*]* @const.0 to i8*), i64 80, i1 false)
-    %index.0 = getelementptr [10 x { i8, [4 x { i64, i64 }*], i8 }*], [10 x { i8, [4 x { i64, i64 }*], i8 }*]* %t1, i32 0, i32 7
-    %t1.index.ld.0 = load { i8, [4 x { i64, i64 }*], i8 }*, { i8, [4 x { i64, i64 }*], i8 }** %index.0, align 8
-    %field.0 = getelementptr inbounds { i8, [4 x { i64, i64 }*], i8 }, { i8, [4 x { i64, i64 }*], i8 }* %t1.index.ld.0, i32 0, i32 1
-    %index.1 = getelementptr [4 x { i64, i64 }*], [4 x { i64, i64 }*]* %field.0, i32 0, i32 3
-    %.field.index.ld.0 = load { i64, i64 }*, { i64, i64 }** %index.1, align 8
-    %field.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %.field.index.ld.0, i32 0, i32 0
-    store i64 5, i64* %field.1, align 8
+      call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %t1, ptr align 8 @const.0, i64 80, i1 false)
+      %index.0 = getelementptr [10 x ptr], ptr %t1, i32 0, i32 7
+      %field.0 = getelementptr inbounds { i8, [4 x ptr], i8 }, ptr %index.0, i32 0, i32 1
+      %index.1 = getelementptr [4 x ptr], ptr %field.0, i32 0, i32 3
+      %field.1 = getelementptr inbounds { i64, i64 }, ptr %index.1, i32 0, i32 0
+      store i64 5, ptr %field.1, align 8
     )RAW_RESULT");
 
     bool isOK = h.expectBlock(exp);
@@ -675,14 +664,12 @@
     h.mkLocal("q", bi64t, fx);
 
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %index.2 = getelementptr [10 x { i8, [4 x { i64, i64 }*], i8 }*], [10 x { i8, [4 x { i64, i64 }*], i8 }*]* %t1, i32 0, i32 0
-    %t1.index.ld.1 = load { i8, [4 x { i64, i64 }*], i8 }*, { i8, [4 x { i64, i64 }*], i8 }** %index.2, align 8
-    %field.2 = getelementptr inbounds { i8, [4 x { i64, i64 }*], i8 }, { i8, [4 x { i64, i64 }*], i8 }* %t1.index.ld.1, i32 0, i32 1
-    %index.3 = getelementptr [4 x { i64, i64 }*], [4 x { i64, i64 }*]* %field.2, i32 0, i32 0
-    %.field.index.ld.1 = load { i64, i64 }*, { i64, i64 }** %index.3, align 8
-    %field.3 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %.field.index.ld.1, i32 0, i32 1
-    %.field.ld.0 = load i64, i64* %field.3, align 8
-    store i64 %.field.ld.0, i64* %q, align 8
+      %index.2 = getelementptr [10 x ptr], ptr %t1, i32 0, i32 0
+      %field.2 = getelementptr inbounds { i8, [4 x ptr], i8 }, ptr %index.2, i32 0, i32 1
+      %index.3 = getelementptr [4 x ptr], ptr %field.2, i32 0, i32 0
+      %field.3 = getelementptr inbounds { i64, i64 }, ptr %index.3, i32 0, i32 1
+      %.field.ld.0 = load i64, ptr %field.3, align 8
+      store i64 %.field.ld.0, ptr %q, align 8
     )RAW_RESULT");
 
     bool isOK = h.expectBlock(exp);
@@ -725,20 +712,12 @@
   h.mkAssign(ve3, ve4);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { i8* }* %x1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i8* }* @const.0 to i8*), i64 8, i1 false)
-    %cast.1 = bitcast { i8* }* %y1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 bitcast ({ i8* }* @const.0 to i8*), i64 8, i1 false)
-    %cast.2 = bitcast { i64, i64, i64, i64, i64, i64 }* %x2 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.2, i8* align 8 bitcast ({ i64, i64, i64, i64, i64, i64 }* @const.1 to i8*), i64 48, i1 false)
-    %cast.3 = bitcast { i64, i64, i64, i64, i64, i64 }* %y2 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.3, i8* align 8 bitcast ({ i64, i64, i64, i64, i64, i64 }* @const.1 to i8*), i64 48, i1 false)
-    %cast.4 = bitcast { i8* }* %x1 to i8*
-    %cast.5 = bitcast { i8* }* %y1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.4, i8* align 8 %cast.5, i64 8, i1 false)
-    %cast.6 = bitcast { i64, i64, i64, i64, i64, i64 }* %x2 to i8*
-    %cast.7 = bitcast { i64, i64, i64, i64, i64, i64 }* %y2 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.6, i8* align 8 %cast.7, i64 48, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %x1, ptr align 8 @const.0, i64 8, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %y1, ptr align 8 @const.0, i64 8, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %x2, ptr align 8 @const.1, i64 48, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %y2, ptr align 8 @const.1, i64 48, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %x1, ptr align 8 %y1, i64 8, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %x2, ptr align 8 %y2, i64 48, i1 false)
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -780,11 +759,10 @@
   h.mkLocal("a2", bpi32t, aex2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { i32 }* %t1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 bitcast ({ i32 }* @const.0 to i8*), i64 4, i1 false)
-    %field.0 = getelementptr inbounds { i32 }, { i32 }* %t1, i32 0, i32 0
-    store i32* %field.0, i32** %a1, align 8
-    store i32* getelementptr inbounds ({ i32 }, { i32 }* @t2, i32 0, i32 0), i32** %a2, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 4 %t1, ptr align 4 @const.0, i64 4, i1 false)
+    %field.0 = getelementptr inbounds { i32 }, ptr %t1, i32 0, i32 0
+    store ptr %field.0, ptr %a1, align 8
+    store ptr @t2, ptr %a2, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
diff --git a/unittests/BackendCore/BackendCABIOracleTests.cpp b/unittests/BackendCore/BackendCABIOracleTests.cpp
index 3f3c902..e675043 100644
--- a/unittests/BackendCore/BackendCABIOracleTests.cpp
+++ b/unittests/BackendCore/BackendCABIOracleTests.cpp
@@ -55,7 +55,7 @@
     CABIOracle cab(befty1, be->typeManager());
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
       Return: Direct { { double, double } } sigOffset: -1
-      Param 1: Direct AttrNest { i8* } sigOffset: 0
+      Param 1: Direct AttrNest { ptr } sigOffset: 0
       Param 2: Direct AttrSext { i8 } sigOffset: 1
       Param 3: Direct { float } sigOffset: 2
       Param 4: Ignore { void } sigOffset: -1
@@ -65,7 +65,7 @@
     bool equal = difftokens(exp.content, cab.toString(), reason);
     EXPECT_EQ("pass", equal ? "pass" : reason);
     EXPECT_EQ(repr(cab.getFunctionTypeForABI()),
-              "{ double, double } (i8*, i8, float, i64)");
+              "{ double, double } (ptr, i8, float, i64)");
   }
 }
 
@@ -118,99 +118,98 @@
   std::vector<FcnItem> items = {
 
       // 1
-      FcnItem( { }, { },
+      FcnItem({}, {},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0",
-              "void (i8*)"),
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0",
+              "void (ptr)"),
 
       // 2
-      FcnItem( { bi8t }, { },
+      FcnItem({bi8t}, {},
               "Return: Direct AttrSext { i8 } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0",
-              "i8 (i8*)"),
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0",
+              "i8 (ptr)"),
 
       // 3
-      FcnItem( { }, { bi8t },
+      FcnItem({}, {bi8t},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct AttrSext { i8 } sigOffset: 1",
-              "void (i8*, i8)"),
+              "void (ptr, i8)"),
 
       // 4
-      FcnItem( { }, { st5, bpf64t },
+      FcnItem({}, {st5, bpf64t},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { float } sigOffset: 1 "
-              "Param 3: Direct { double* } sigOffset: 2",
-              "void (i8*, float, double*)"),
+              "Param 3: Direct { ptr } sigOffset: 2",
+              "void (ptr, float, ptr)"),
 
       // 5
-      FcnItem({ bi8t, bf64t }, { bi8t, bu8t, st0 },
+      FcnItem({bi8t, bf64t}, {bi8t, bu8t, st0},
               "Return: Direct { { i8, double } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct AttrSext { i8 } sigOffset: 1 "
               "Param 3: Direct AttrZext { i8 } sigOffset: 2 "
               "Param 4: Ignore { void } sigOffset: -1",
-              "{ i8, double } (i8*, i8, i8)"),
+              "{ i8, double } (ptr, i8, i8)"),
 
       // 6
-      FcnItem({ st2 }, { st2, st0, st4, st1 },
+      FcnItem({st2}, {st2, st0, st4, st1},
               "Return: Direct { { double, double } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { double, double } sigOffset: 1 "
               "Param 3: Ignore { void } sigOffset: -1 "
               "Param 4: Direct { <2 x float> } sigOffset: 3 "
               "Param 5: Direct { i64 } sigOffset: 4 ",
-              "{ double, double } (i8*, double, double, <2 x float>, i64)"),
+              "{ double, double } (ptr, double, double, <2 x float>, i64)"),
 
       // 7
-      FcnItem({ st3 }, { st3, st0, bu8t },
-              "Return: Indirect AttrStructReturn { { { double, double }, i8 }* } sigOffset: 0 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 1 "
-              "Param 2: Indirect AttrByVal { { { double, double }, i8 }* } sigOffset: 2 "
+      FcnItem({st3}, {st3, st0, bu8t},
+              "Return: Indirect AttrStructReturn { ptr } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 1 "
+              "Param 2: Indirect AttrByVal { ptr } sigOffset: 2 "
               "Param 3: Ignore { void } sigOffset: -1 "
               "Param 4: Direct AttrZext { i8 } sigOffset: 3 ",
-              "void ({ { double, double }, i8 }*, i8*, "
-              "{ { double, double }, i8 }*, i8)"),
+              "void (ptr, ptr, ptr, i8)"),
 
       // 8
-      FcnItem( { st6 }, { st6, st6 },
+      FcnItem({st6}, {st6, st6},
               "Return: Direct { { i64, i64 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1 "
               "Param 3: Direct { i64, i64 } sigOffset: 3",
-              "{ i64, i64 } (i8*, i64, i64, i64, i64)"),
+              "{ i64, i64 } (ptr, i64, i64, i64, i64)"),
 
       // 9
-      FcnItem( { st8 }, { st8 },
+      FcnItem({st8}, {st8},
               "Return: Direct { { i64, i32 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i32 } sigOffset: 1",
-              "{ i64, i32 } (i8*, i64, i32)"),
+              "{ i64, i32 } (ptr, i64, i32)"),
 
       // 10
-      FcnItem( { at0 }, { at1 },
+      FcnItem({at0}, {at1},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i32 } sigOffset: 1",
-              "void (i8*, i32)"),
+              "void (ptr, i32)"),
 
       // 11
-      FcnItem( { at2 }, { at3 },
+      FcnItem({at2}, {at3},
               "Return: Direct { { i64, i32 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1",
-              "{ i64, i32 } (i8*, i64, i64)"),
+              "{ i64, i32 } (ptr, i64, i64)"),
 
       // 12
       // Make sure pointerness is preserved.
-      FcnItem( { stip }, { stii, stpp, stpi },
-              "Return: Direct { { i64, i8* } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+      FcnItem({stip}, {stii, stpp, stpi},
+              "Return: Direct { { i64, ptr } } sigOffset: -1 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1 "
-              "Param 3: Direct { i8*, i8* } sigOffset: 3 "
-              "Param 4: Direct { i8*, i64 } sigOffset: 5",
-              "{ i64, i8* } (i8*, i64, i64, i8*, i8*, i8*, i64)"),
+              "Param 3: Direct { ptr, ptr } sigOffset: 3 "
+              "Param 4: Direct { ptr, i64 } sigOffset: 5",
+              "{ i64, ptr } (ptr, i64, i64, ptr, ptr, ptr, i64)"),
   };
 
   unsigned count = 1;
@@ -303,99 +302,97 @@
       // 1
       FcnItem({}, {},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0",
-              "void (i8*)"),
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0",
+              "void (ptr)"),
 
       // 2
       FcnItem({bi8t}, {},
               "Return: Direct AttrSext { i8 } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0",
-              "i8 (i8*)"),
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0",
+              "i8 (ptr)"),
 
       // 3
       FcnItem({}, {bi8t},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct AttrSext { i8 } sigOffset: 1",
-              "void (i8*, i8)"),
+              "void (ptr, i8)"),
 
       // 4
       FcnItem({}, {st5, bpf64t},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { float } sigOffset: 1 "
-              "Param 3: Direct { double* } sigOffset: 2",
-              "void (i8*, float, double*)"),
+              "Param 3: Direct { ptr } sigOffset: 2",
+              "void (ptr, float, ptr)"),
 
       // 5
       FcnItem({bi8t, bf64t}, {bi8t, bu8t, st0},
               "Return: Direct { { i64, i64 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct AttrSext { i8 } sigOffset: 1 "
               "Param 3: Direct AttrZext { i8 } sigOffset: 2 "
               "Param 4: Ignore { void } sigOffset: -1",
-              "{ i64, i64 } (i8*, i8, i8)"),
+              "{ i64, i64 } (ptr, i8, i8)"),
 
       // 6
       FcnItem({st2}, {st2, st0, st4, st1},
               "Return: Direct { { double, double } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { [2 x double] } sigOffset: 1 "
               "Param 3: Ignore { void } sigOffset: -1 "
               "Param 4: Direct { [2 x float] } sigOffset: 2 "
               "Param 5: Direct { i64 } sigOffset:  3",
-              "{ double, double } (i8*, [2 x double], [2 x float], i64)"),
+              "{ double, double } (ptr, [2 x double], [2 x float], i64)"),
 
       // 7
       FcnItem({st3}, {st3, st0, bu8t},
-              "Return: Indirect AttrStructReturn { { { double, double }, i8 "
-              "}* } sigOffset: 0 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 1 "
-              "Param 2: Indirect AttrDoCopy { { { double, double }, i8 }* } "
+              "Return: Indirect AttrStructReturn { ptr } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 1 "
+              "Param 2: Indirect AttrDoCopy { ptr } "
               "sigOffset: 2 "
               "Param 3: Ignore { void } sigOffset: -1 "
               "Param 4: Direct AttrZext { i8 } sigOffset: 3 ",
-              "void ({ { double, double }, i8 }*, i8*, "
-              "{ { double, double }, i8 }*, i8)"),
+              "void (ptr, ptr, ptr, i8)"),
 
       // 8
       FcnItem({st6}, {st6, st6},
               "Return: Direct { { i64, i64 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1 "
               "Param 3: Direct { i64, i64 } sigOffset: 3",
-              "{ i64, i64 } (i8*, i64, i64, i64, i64)"),
+              "{ i64, i64 } (ptr, i64, i64, i64, i64)"),
 
       // 9
       FcnItem({st8}, {st8},
               "Return: Direct { { i64, i32 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i32 } sigOffset: 1",
-              "{ i64, i32 } (i8*, i64, i32)"),
+              "{ i64, i32 } (ptr, i64, i32)"),
 
       // 10
       FcnItem({at0}, {at1},
               "Return: Ignore { void } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i32 } sigOffset: 1",
-              "void (i8*, i32)"),
+              "void (ptr, i32)"),
 
       // 11
       FcnItem({at2}, {at3},
               "Return: Direct { { i64, i32 } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1",
-              "{ i64, i32 } (i8*, i64, i64)"),
+              "{ i64, i32 } (ptr, i64, i64)"),
 
       // 12
       // Make sure pointerness is preserved.
       FcnItem({stip}, {stii, stpp, stpi},
-              "Return: Direct { { i64, i8* } } sigOffset: -1 "
-              "Param 1: Direct AttrNest { i8* } sigOffset: 0 "
+              "Return: Direct { { i64, ptr } } sigOffset: -1 "
+              "Param 1: Direct AttrNest { ptr } sigOffset: 0 "
               "Param 2: Direct { i64, i64 } sigOffset: 1 "
-              "Param 3: Direct { i8*, i8* } sigOffset: 3 "
-              "Param 4: Direct { i8*, i64 } sigOffset: 5",
-              "{ i64, i8* } (i8*, i64, i64, i8*, i8*, i8*, i64)"),
+              "Param 3: Direct { ptr, ptr } sigOffset: 3 "
+              "Param 4: Direct { ptr, i64 } sigOffset: 5",
+              "{ i64, ptr } (ptr, i64, i64, ptr, ptr, ptr, i64)"),
   };
 
   unsigned count = 1;
@@ -532,24 +529,20 @@
   Bstatement *rst2 = h.mkReturn(rvals2, FcnTestHarness::NoAppend);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %p3.ld.0 = load i8, i8* %p3.addr, align 1
+    %p3.ld.0 = load i8, ptr %p3.addr, align 1
     %sub.0 = sub i8 %p3.ld.0, 1
-    %p4.ld.0 = load i8, i8* %p4.addr, align 1
-    %cast.1 = bitcast { float, float, i16, i16, i16 }* %p0.addr to { <2 x float>, i48 }*
-    %field0.0 = getelementptr inbounds { <2 x float>, i48 }, { <2 x float>, i48 }* %cast.1, i32 0, i32 0
-    %ld.1 = load <2 x float>, <2 x float>* %field0.0, align 8
-    %field1.0 = getelementptr inbounds { <2 x float>, i48 }, { <2 x float>, i48 }* %cast.1, i32 0, i32 1
-    %ld.2 = load i48, i48* %field1.0, align 8
-    %cast.2 = bitcast { double, float, float }* %p1.addr to { double, <2 x float> }*
-    %field0.1 = getelementptr inbounds { double, <2 x float> }, { double, <2 x float> }* %cast.2, i32 0, i32 0
-    %ld.3 = load double, double* %field0.1, align 8
-    %field1.1 = getelementptr inbounds { double, <2 x float> }, { double, <2 x float> }* %cast.2, i32 0, i32 1
-    %ld.4 = load <2 x float>, <2 x float>* %field1.1, align 8
-    %call.0 = call addrspace(0) { double, <2 x float> } @foo(i8* nest undef, <2 x float> %ld.1, i48 %ld.2, double %ld.3, <2 x float> %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, { { float, float, i16, i16, i16 }, { double, float, float } }* byval({ { float, float, i16, i16, i16 }, { double, float, float } }) %p5)
-    %cast.3 = bitcast { double, float, float }* %sret.actual.0 to { double, <2 x float> }*
-    store { double, <2 x float> } %call.0, { double, <2 x float> }* %cast.3, align 8
-    %cast.4 = bitcast { double, float, float }* %sret.actual.0 to { double, <2 x float> }*
-    %ld.5 = load { double, <2 x float> }, { double, <2 x float> }* %cast.4, align 8
+    %p4.ld.0 = load i8, ptr %p4.addr, align 1
+    %field0.0 = getelementptr inbounds { <2 x float>, i48 }, ptr %p0.addr, i32 0, i32 0
+    %ld.1 = load <2 x float>, ptr %field0.0, align 8
+    %field1.0 = getelementptr inbounds { <2 x float>, i48 }, ptr %p0.addr, i32 0, i32 1
+    %ld.2 = load i48, ptr %field1.0, align 8
+    %field0.1 = getelementptr inbounds { double, <2 x float> }, ptr %p1.addr, i32 0, i32 0
+    %ld.3 = load double, ptr %field0.1, align 8
+    %field1.1 = getelementptr inbounds { double, <2 x float> }, ptr %p1.addr, i32 0, i32 1
+    %ld.4 = load <2 x float>, ptr %field1.1, align 8
+    %call.0 = call addrspace(0) { double, <2 x float> } @foo(ptr nest undef, <2 x float> %ld.1, i48 %ld.2, double %ld.3, <2 x float> %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, ptr byval({ { float, float, i16, i16, i16 }, { double, float, float } }) %p5)
+    store { double, <2 x float> } %call.0, ptr %sret.actual.0, align 8
+    %ld.5 = load { double, <2 x float> }, ptr %sret.actual.0, align 8
     ret { double, <2 x float> } %ld.5
   )RAW_RESULT");
 
@@ -651,27 +644,21 @@
   Bstatement *rst2 = h.mkReturn(rvals2, FcnTestHarness::NoAppend);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %p3.ld.0 = load i8, i8* %p3.addr, align 1
+    %p3.ld.0 = load i8, ptr %p3.addr, align 1
     %sub.0 = sub i8 %p3.ld.0, 1
-    %p4.ld.0 = load i8, i8* %p4.addr, align 1
-    %cast.1 = bitcast { float, float, i16, i16, i16 }* %p0.addr to { i64, i48 }*
-    %field0.0 = getelementptr inbounds { i64, i48 }, { i64, i48 }* %cast.1, i32 0, i32 0
-    %ld.1 = load i64, i64* %field0.0, align 8
-    %field1.0 = getelementptr inbounds { i64, i48 }, { i64, i48 }* %cast.1, i32 0, i32 1
-    %ld.2 = load i48, i48* %field1.0, align 8
-    %cast.2 = bitcast { double, float, float }* %p1.addr to { i64, i64 }*
-    %field0.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %cast.2, i32 0, i32 0
-    %ld.3 = load i64, i64* %field0.1, align 8
-    %field1.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %cast.2, i32 0, i32 1
-    %ld.4 = load i64, i64* %field1.1, align 8
-    %cast.3 = bitcast { { float, float, i16, i16, i16 }, { double, float, float } }* %doCopy.addr.0 to i8*
-    %cast.4 = bitcast { { float, float, i16, i16, i16 }, { double, float, float } }* %p5 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.3, i8* align 8 %cast.4, i64 32, i1 false)
-    %call.0 = call addrspace(0) { i64, i64 } @foo(i8* nest undef, i64 %ld.1, i48 %ld.2, i64 %ld.3, i64 %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, { { float, float, i16, i16, i16 }, { double, float, float } }* %doCopy.addr.0)
-    %cast.5 = bitcast { double, float, float }* %sret.actual.0 to { i64, i64 }*
-    store { i64, i64 } %call.0, { i64, i64 }* %cast.5, align 8
-    %cast.6 = bitcast { double, float, float }* %sret.actual.0 to { i64, i64 }*
-    %ld.5 = load { i64, i64 }, { i64, i64 }* %cast.6, align 8
+    %p4.ld.0 = load i8, ptr %p4.addr, align 1
+    %field0.0 = getelementptr inbounds { i64, i48 }, ptr %p0.addr, i32 0, i32 0
+    %ld.1 = load i64, ptr %field0.0, align 8
+    %field1.0 = getelementptr inbounds { i64, i48 }, ptr %p0.addr, i32 0, i32 1
+    %ld.2 = load i48, ptr %field1.0, align 8
+    %field0.1 = getelementptr inbounds { i64, i64 }, ptr %p1.addr, i32 0, i32 0
+    %ld.3 = load i64, ptr %field0.1, align 8
+    %field1.1 = getelementptr inbounds { i64, i64 }, ptr %p1.addr, i32 0, i32 1
+    %ld.4 = load i64, ptr %field1.1, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %doCopy.addr.0, ptr align 8 %p5, i64 32, i1 false)
+    %call.0 = call addrspace(0) { i64, i64 } @foo(ptr nest undef, i64 %ld.1, i48 %ld.2, i64 %ld.3, i64 %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, ptr %doCopy.addr.0)
+    store { i64, i64 } %call.0, ptr %sret.actual.0, align 8
+    %ld.5 = load { i64, i64 }, ptr %sret.actual.0, align 8
     ret { i64, i64 } %ld.5
   )RAW_RESULT");
 
@@ -716,12 +703,9 @@
   h.mkReturn(rvals);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast [2 x float]* %p0.addr to <2 x float>*
-    %ld.0 = load <2 x float>, <2 x float>* %cast.0, align 8
-    call addrspace(0) void @foo([3 x double]* sret([3 x double]) "go_sret" %sret.actual.0, i8* nest undef, <2 x float> %ld.0)
-    %cast.1 = bitcast [3 x double]* %sret.formal.0 to i8*
-    %cast.2 = bitcast [3 x double]* %sret.actual.0 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 %cast.2, i64 24, i1 false)
+    %ld.0 = load <2 x float>, ptr %p0.addr, align 8
+    call addrspace(0) void @foo(ptr sret([3 x double]) "go_sret" %sret.actual.0, ptr nest undef, <2 x float> %ld.0)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %sret.formal.0, ptr align 8 %sret.actual.0, i64 24, i1 false)
     ret void
   )RAW_RESULT");
 
@@ -760,12 +744,10 @@
   h.mkReturn(rvals);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %ld.0 = load [2 x float], [2 x float]* %p0.addr, align 4
-    %call.0 = call addrspace(0) { double, double, double } @foo(i8* nest undef, [2 x float] %ld.0)
-    %cast.1 = bitcast [3 x double]* %sret.actual.0 to { double, double, double }*
-    store { double, double, double } %call.0, { double, double, double }* %cast.1, align 8
-    %cast.2 = bitcast [3 x double]* %sret.actual.0 to { double, double, double }*
-    %ld.1 = load { double, double, double }, { double, double, double }* %cast.2, align 8
+    %ld.0 = load [2 x float], ptr %p0.addr, align 4
+    %call.0 = call addrspace(0) { double, double, double } @foo(ptr nest undef, [2 x float] %ld.0)
+    store { double, double, double } %call.0, ptr %sret.actual.0, align 8
+    %ld.1 = load { double, double, double }, ptr %sret.actual.0, align 8
     ret { double, double, double } %ld.1
   )RAW_RESULT");
 
@@ -813,7 +795,7 @@
   h.mkReturn(rvals);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    call addrspace(0) void @foo(i8* nest undef, i32 4)
+    call addrspace(0) void @foo(ptr nest undef, i32 4)
     ret void
   )RAW_RESULT");
 
@@ -895,26 +877,20 @@
   h.mkReturn(rvals);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { float, float }* %p0.addr to <2 x float>*
-    %ld.0 = load <2 x float>, <2 x float>* %cast.0, align 8
-    %field0.0 = getelementptr inbounds { double, double }, { double, double }* %p1.addr, i32 0, i32 0
-    %ld.1 = load double, double* %field0.0, align 8
-    %field1.0 = getelementptr inbounds { double, double }, { double, double }* %p1.addr, i32 0, i32 1
-    %ld.2 = load double, double* %field1.0, align 8
-    %call.0 = call addrspace(0) <2 x float> @foo(i8* nest undef, <2 x float> %ld.0, double %ld.1, double %ld.2)
-    %cast.2 = bitcast { float, float }* %sret.actual.0 to <2 x float>*
-    store <2 x float> %call.0, <2 x float>* %cast.2, align 8
-    %cast.3 = bitcast { float, float }* %z to i8*
-    %cast.4 = bitcast { float, float }* %sret.actual.0 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 8, i1 false)
-    %ld.3 = load <2 x float>, <2 x float>* bitcast ({ float, float }* @const.0 to <2 x float>*), align 8
-    %ld.4 = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @const.1, i32 0, i32 0), align 8
-    %ld.5 = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @const.1, i32 0, i32 1), align 8
-    %call.1 = call addrspace(0) <2 x float> @foo(i8* nest undef, <2 x float> %ld.3, double %ld.4, double %ld.5)
-    %cast.7 = bitcast { float, float }* %sret.actual.1 to <2 x float>*
-    store <2 x float> %call.1, <2 x float>* %cast.7, align 8
-    %cast.8 = bitcast { float, float }* %sret.actual.1 to <2 x float>*
-    %ld.6 = load <2 x float>, <2 x float>* %cast.8, align 8
+    %ld.0 = load <2 x float>, ptr %p0.addr, align 8
+    %field0.0 = getelementptr inbounds { double, double }, ptr %p1.addr, i32 0, i32 0
+    %ld.1 = load double, ptr %field0.0, align 8
+    %field1.0 = getelementptr inbounds { double, double }, ptr %p1.addr, i32 0, i32 1
+    %ld.2 = load double, ptr %field1.0, align 8
+    %call.0 = call addrspace(0) <2 x float> @foo(ptr nest undef, <2 x float> %ld.0, double %ld.1, double %ld.2)
+    store <2 x float> %call.0, ptr %sret.actual.0, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 4 %z, ptr align 4 %sret.actual.0, i64 8, i1 false)
+    %ld.3 = load <2 x float>, ptr @const.0, align 8
+    %ld.4 = load double, ptr @const.1, align 8
+    %ld.5 = load double, ptr getelementptr inbounds ({ double, double }, ptr @const.1, i32 0, i32 1), align 8
+    %call.1 = call addrspace(0) <2 x float> @foo(ptr nest undef, <2 x float> %ld.3, double %ld.4, double %ld.5)
+    store <2 x float> %call.1, ptr %sret.actual.1, align 8
+    %ld.6 = load <2 x float>, ptr %sret.actual.1, align 8
     ret <2 x float> %ld.6
   )RAW_RESULT");
 
@@ -968,20 +944,16 @@
   h.mkReturn(rvals);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { float, float }* %p0.addr to [2 x float]*
-    %ld.0 = load [2 x float], [2 x float]* %cast.0, align 4
-    %cast.1 = bitcast { double, double }* %p1.addr to [2 x double]*
-    %ld.1 = load [2 x double], [2 x double]* %cast.1, align 8
-    %call.0 = call addrspace(0) { float, float } @foo(i8* nest undef, [2 x float] %ld.0, [2 x double] %ld.1)
-    store { float, float } %call.0, { float, float }* %sret.actual.0, align 4
-    %cast.3 = bitcast { float, float }* %z to i8*
-    %cast.4 = bitcast { float, float }* %sret.actual.0 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 8, i1 false)
-    %ld.2 = load [2 x float], [2 x float]* bitcast ({ float, float }* @const.0 to [2 x float]*), align 4
-    %ld.3 = load [2 x double], [2 x double]* bitcast ({ double, double }* @const.1 to [2 x double]*), align 8
-    %call.1 = call addrspace(0) { float, float } @foo(i8* nest undef, [2 x float] %ld.2, [2 x double] %ld.3)
-    store { float, float } %call.1, { float, float }* %sret.actual.1, align 4
-    %ld.4 = load { float, float }, { float, float }* %sret.actual.1, align 4
+    %ld.0 = load [2 x float], ptr %p0.addr, align 4
+    %ld.1 = load [2 x double], ptr %p1.addr, align 8
+    %call.0 = call addrspace(0) { float, float } @foo(ptr nest undef, [2 x float] %ld.0, [2 x double] %ld.1)
+    store { float, float } %call.0, ptr %sret.actual.0, align 4
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 4 %z, ptr align 4 %sret.actual.0, i64 8, i1 false)
+    %ld.2 = load [2 x float], ptr @const.0, align 4
+    %ld.3 = load [2 x double], ptr @const.1, align 8
+    %call.1 = call addrspace(0) { float, float } @foo(ptr nest undef, [2 x float] %ld.2, [2 x double] %ld.3)
+    store { float, float } %call.1, ptr %sret.actual.1, align 4
+    %ld.4 = load { float, float }, ptr %sret.actual.1, align 4
     ret { float, float } %ld.4
   )RAW_RESULT");
 
diff --git a/unittests/BackendCore/BackendCallTests.cpp b/unittests/BackendCore/BackendCallTests.cpp
index b8d7eaf..bd9da2a 100644
--- a/unittests/BackendCore/BackendCallTests.cpp
+++ b/unittests/BackendCore/BackendCallTests.cpp
@@ -47,9 +47,9 @@
   h.mkReturn(be->var_expression(x, loc));
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %call.0 = call addrspace(0) i64 @foo(i8* nest undef, i32 3, i32 6, i64* null)
-    store i64 %call.0, i64* %x, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
+    %call.0 = call addrspace(0) i64 @foo(ptr nest undef, i32 3, i32 6, ptr null)
+    store i64 %call.0, ptr %x, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
     ret i64 %x.ld.0
   )RAW_RESULT");
 
@@ -80,7 +80,7 @@
   h.mkExprStmt(call);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    call addrspace(0) void @bar(i8* nest undef)
+    call addrspace(0) void @bar(ptr nest undef)
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -119,8 +119,7 @@
 
   {
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-      %cast.0 = bitcast { i8*, i32*, i64*, i64 }* %sret.formal.0 to i8*
-      call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i8*, i32*, i64*, i64 }* @const.0 to i8*), i64 32, i1 false)
+      call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %sret.formal.0, ptr align 8 @const.0, i64 32, i1 false)
       ret void
     )RAW_RESULT");
 
@@ -143,19 +142,17 @@
 
   {
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %p0.ld.0 = load i8*, i8** %p0.addr, align 8
-    %field.0 = getelementptr inbounds { i8*, i32*, i64*, i64 }, { i8*, i32*, i64*, i64 }* %tmp.0, i32 0, i32 0
-    store i8* %p0.ld.0, i8** %field.0, align 8
-    %field.1 = getelementptr inbounds { i8*, i32*, i64*, i64 }, { i8*, i32*, i64*, i64 }* %tmp.0, i32 0, i32 1
-    store i32* null, i32** %field.1, align 8
-    %field.2 = getelementptr inbounds { i8*, i32*, i64*, i64 }, { i8*, i32*, i64*, i64 }* %tmp.0, i32 0, i32 2
-    store i64* null, i64** %field.2, align 8
-    %field.3 = getelementptr inbounds { i8*, i32*, i64*, i64 }, { i8*, i32*, i64*, i64 }* %tmp.0, i32 0, i32 3
-    store i64 101, i64* %field.3, align 8
-    %cast.2 = bitcast { i8*, i32*, i64*, i64 }* %sret.formal.0 to i8*
-    %cast.3 = bitcast { i8*, i32*, i64*, i64 }* %tmp.0 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.2, i8* align 8 %cast.3, i64 32, i1 false)
-    ret void
+      %p0.ld.0 = load ptr, ptr %p0.addr, align 8
+      %field.0 = getelementptr inbounds { ptr, ptr, ptr, i64 }, ptr %tmp.0, i32 0, i32 0
+      store ptr %p0.ld.0, ptr %field.0, align 8
+      %field.1 = getelementptr inbounds { ptr, ptr, ptr, i64 }, ptr %tmp.0, i32 0, i32 1
+      store ptr null, ptr %field.1, align 8
+      %field.2 = getelementptr inbounds { ptr, ptr, ptr, i64 }, ptr %tmp.0, i32 0, i32 2
+      store ptr null, ptr %field.2, align 8
+      %field.3 = getelementptr inbounds { ptr, ptr, ptr, i64 }, ptr %tmp.0, i32 0, i32 3
+      store i64 101, ptr %field.3, align 8
+      call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %sret.formal.0, ptr align 8 %tmp.0, i64 32, i1 false)
+      ret void
     )RAW_RESULT");
 
     bool isOK = h.expectStmt(s2, exp);
@@ -206,12 +203,12 @@
          FcnTestHarness::YesAppend);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+    define void @foo(ptr nest %nest.0) #0 {
     entry:
       br i1 true, label %then.0, label %else.0
 
     then.0:                                           ; preds = %entry
-      call void @noret(i8* nest undef)
+      call void @noret(ptr nest undef)
       unreachable
 
     fallthrough.0:                                    ; preds = %else.0
@@ -283,18 +280,18 @@
   h.mkReturn(be->var_expression(z, loc));
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %asmcall.0 = call addrspace(0) i64* asm sideeffect "adrp x0, :tlsdesc:runtime.g\0Aldr  $0, [x0, :tlsdesc_lo12:runtime.g]\0Aadd  x0, x0, :tlsdesc_lo12:runtime.g\0A.tlsdesccall runtime.g\0Ablr  $0\0Amrs  $0, TPIDR_EL0\0Aldr  $0, [$0, x0]\0A", "=r,~{x0}"()
-    store i64* %asmcall.0, i64** %x, align 8
-    call addrspace(0) void @bar(i8* nest undef)
-    %asmcall.1 = call addrspace(0) i64* asm sideeffect "adrp x0, :tlsdesc:runtime.g\0Aldr  $0, [x0, :tlsdesc_lo12:runtime.g]\0Aadd  x0, x0, :tlsdesc_lo12:runtime.g\0A.tlsdesccall runtime.g\0Ablr  $0\0Amrs  $0, TPIDR_EL0\0Aldr  $0, [$0, x0]\0A", "=r,~{x0}"()
-    store i64* %asmcall.1, i64** %y, align 8
-    %x.ld.0 = load i64*, i64** %x, align 8
-    %.ld.0 = load i64, i64* %x.ld.0, align 8
-    %y.ld.0 = load i64*, i64** %y, align 8
-    %.ld.1 = load i64, i64* %y.ld.0, align 8
+    %asmcall.0 = call addrspace(0) ptr asm sideeffect "adrp x0, :tlsdesc:runtime.g\0Aldr  $0, [x0, :tlsdesc_lo12:runtime.g]\0Aadd  x0, x0, :tlsdesc_lo12:runtime.g\0A.tlsdesccall runtime.g\0Ablr  $0\0Amrs  $0, TPIDR_EL0\0Aldr  $0, [$0, x0]\0A", "=r,~{x0}"()
+    store ptr %asmcall.0, ptr %x, align 8
+    call addrspace(0) void @bar(ptr nest undef)
+    %asmcall.1 = call addrspace(0) ptr asm sideeffect "adrp x0, :tlsdesc:runtime.g\0Aldr  $0, [x0, :tlsdesc_lo12:runtime.g]\0Aadd  x0, x0, :tlsdesc_lo12:runtime.g\0A.tlsdesccall runtime.g\0Ablr  $0\0Amrs  $0, TPIDR_EL0\0Aldr  $0, [$0, x0]\0A", "=r,~{x0}"()
+    store ptr %asmcall.1, ptr %y, align 8
+    %x.ld.0 = load ptr, ptr %x, align 8
+    %.ld.0 = load i64, ptr %x.ld.0, align 8
+    %y.ld.0 = load ptr, ptr %y, align 8
+    %.ld.1 = load i64, ptr %y.ld.0, align 8
     %add.0 = add i64 %.ld.0, %.ld.1
-    store i64 %add.0, i64* %z, align 8
-    %z.ld.0 = load i64, i64* %z, align 8
+    store i64 %add.0, ptr %z, align 8
+    %z.ld.0 = load i64, ptr %z, align 8
     ret i64 %z.ld.0
   )RAW_RESULT");
 
diff --git a/unittests/BackendCore/BackendCoreTests.cpp b/unittests/BackendCore/BackendCoreTests.cpp
index 30923a0..9ca75f5 100644
--- a/unittests/BackendCore/BackendCoreTests.cpp
+++ b/unittests/BackendCore/BackendCoreTests.cpp
@@ -86,7 +86,7 @@
   ASSERT_TRUE(best != nullptr);
   ASSERT_TRUE(llst != nullptr);
   EXPECT_EQ(llst, best->type());
-  EXPECT_EQ(repr(best->type()), "{ i8, float*, i64 }");
+  EXPECT_EQ(repr(best->type()), "{ i8, ptr, i64 }");
 
   // If a field has error type, entire struct has error type
   std::vector<Backend::Btyped_identifier> fields = {
@@ -186,14 +186,14 @@
   Btype *phpt2 = be->placeholder_pointer_type("ph", loc, false);
   Btype *phpt3 = be->placeholder_pointer_type("ph", loc, false);
   ASSERT_TRUE(phpt2 != phpt3);
-  EXPECT_TRUE(phpt2->type() != phpt3->type());
+  // ptr == ptr
+  EXPECT_TRUE(phpt2->type() == phpt3->type());
 
   // Replace placeholder pointer type
   Btype *pst = be->pointer_type(mkBackendThreeFieldStruct(be.get()));
   be->set_placeholder_pointer_type(phpt1, pst);
   ASSERT_TRUE(phpt1->type()->isPointerTy());
   PointerType *llpt = cast<PointerType>(phpt1->type());
-  EXPECT_TRUE(llpt->getElementType()->isStructTy());
 
   // Placeholder struct type
   Btype *phst1 = be->placeholder_struct_type("ph", loc);
@@ -398,8 +398,6 @@
   // However the underlying LLVM types should be considered
   // assignment-compatible.
   std::set<llvm::Type *> visited;
-  bool equiv = tm->fcnPointerCompatible(pht->type(), pht2->type(), visited);
-  EXPECT_TRUE(equiv);
 
   bool broken = h.finish(PreserveDebugInfo);
   EXPECT_FALSE(broken && "Module failed to verify.");
@@ -468,9 +466,9 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   bool ok = h.expectModuleDumpContains(
-      "@gv = global { { i8, i8*, i32 }, %ph.0 } { { i8, i8*, i32 } { i8 0, "
-      "i8* "
-      "null, i32 101 }, %ph.0 { i8 0, i8* null, i32 101 } }");
+      "@gv = global { { i8, ptr, i32 }, %ph.0 } { { i8, ptr, i32 } { i8 0, "
+      "ptr "
+      "null, i32 101 }, %ph.0 { i8 0, ptr null, i32 101 } }");
   EXPECT_TRUE(ok);
 }
 
diff --git a/unittests/BackendCore/BackendDebugEmit.cpp b/unittests/BackendCore/BackendDebugEmit.cpp
index 7859104..f880cb2 100644
--- a/unittests/BackendCore/BackendDebugEmit.cpp
+++ b/unittests/BackendCore/BackendDebugEmit.cpp
@@ -42,13 +42,13 @@
   h.mkLocal("x", bu32t);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
-  entry:
-    %x = alloca i32, align 4
-    store i32 0, i32* %x, align 4
-    call void @llvm.dbg.declare(metadata i32* %x, metadata !4, metadata !DIExpression()), !dbg !12
-    ret void
-  }
+    define void @foo(ptr nest %nest.0) #0 {
+    entry:
+      %x = alloca i32, align 4
+      store i32 0, ptr %x, align 4
+        #dbg_declare(ptr %x, !4, !DIExpression(), !12)
+      ret void
+    }
   )RAW_RESULT");
 
   bool broken = h.finish(PreserveDebugInfo);
@@ -76,11 +76,11 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0, { i64, i64, i64 }* byval({ i64, i64, i64 }) %p0) #0 {
-  entry:
-    call void @llvm.dbg.declare(metadata { i64, i64, i64 }* %p0, metadata !4, metadata !DIExpression()), !dbg !18
-    ret void
-  }
+    define void @foo(ptr nest %nest.0, ptr byval({ i64, i64, i64 }) %p0) #0 {
+    entry:
+        #dbg_declare(ptr %p0, !4, !DIExpression(), !18)
+      ret void
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -103,10 +103,9 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0, { i64, i64, i64 }* %p0) #0 {
+    define void @foo(ptr nest %nest.0, ptr %p0) #0 {
     entry:
-      call void @llvm.dbg.declare(metadata { i64, i64, i64 }* %p0, metadata !4,
-                                  metadata !DIExpression()), !dbg !18
+        #dbg_declare(ptr %p0, !4, !DIExpression(), !18)
       ret void
     }
   )RAW_RESULT");
@@ -188,7 +187,7 @@
   std::vector<std::string> tokens = tokenize(fdump);
   unsigned declcount = 0;
   for (auto t : tokens)
-    if (t == "@llvm.dbg.declare(metadata")
+    if (t == "#dbg_declare(ptr")
       declcount += 1;
 
   // seven formals and six locals => 13 var decls
@@ -210,11 +209,11 @@
   h.mkLocal("x", bu32t);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 !dbg !4 {
-  entry:
-    %x = alloca i32, align 4
-    ret void, !dbg !10
-  }
+    define void @foo(ptr nest %nest.0) #0 !dbg !4 {
+    entry:
+      %x = alloca i32, align 4
+      ret void, !dbg !10
+    }
   )RAW_RESULT");
 
   bool broken = h.finish(PreserveDebugInfo);
diff --git a/unittests/BackendCore/BackendExprTests.cpp b/unittests/BackendCore/BackendExprTests.cpp
index a476f06..5ead97e 100644
--- a/unittests/BackendCore/BackendExprTests.cpp
+++ b/unittests/BackendCore/BackendExprTests.cpp
@@ -196,7 +196,7 @@
   Btype *pbt = be->pointer_type(bt);
   Bexpression *bpzero = be->zero_expression(pbt);
   ASSERT_TRUE(bpzero != nullptr);
-  EXPECT_EQ(repr(bpzero->value()), "i8* null");
+  EXPECT_EQ(repr(bpzero->value()), "ptr null");
   Btype *bi32t = be->integer_type(false, 32);
   Btype *s2t = mkBackendStruct(be, pbt, "f1", bi32t, "f2", nullptr);
   Bexpression *bszero = be->zero_expression(s2t);
@@ -244,10 +244,9 @@
   h.mkAssign(fex, mkInt32Const(be, 22));
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    %cast.0 = bitcast i64* %x to { i32, i32 }*
-    %field.0 = getelementptr inbounds { i32, i32 }, { i32, i32 }* %cast.0, i32 0, i32 1
-    store i32 22, i32* %field.0, align 4
+    store i64 0, ptr %x, align 8
+    %field.0 = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 1
+    store i32 22, ptr %field.0, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -295,14 +294,13 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %param3.ld.0 = load i64*, i64** %param3.addr, align 8
-    %cast.0 = bitcast i64* %param3.ld.0 to i32*
-    store i32 5, i32* %cast.0, align 4
-    store double 0.000000e+00, double* %p, align 8
-    %p.ld.0 = load double, double* %p, align 8
+    %param3.ld.0 = load ptr, ptr %param3.addr, align 8
+    store i32 5, ptr %param3.ld.0, align 4
+    store double 0.000000e+00, ptr %p, align 8
+    %p.ld.0 = load double, ptr %p, align 8
     %ftoui.0 = fptoui double %p.ld.0 to i64
-    %itpcast.0 = inttoptr i64 %ftoui.0 to i32*
-    store i32 5, i32* %itpcast.0, align 4
+    %itpcast.0 = inttoptr i64 %ftoui.0 to ptr
+    store i32 5, ptr %itpcast.0, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -354,92 +352,92 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %p1.ld.0 = load double, double* %p1.addr, align 8
+    %p1.ld.0 = load double, ptr %p1.addr, align 8
     %fptrunc.0 = fptrunc double %p1.ld.0 to float
-    store float %fptrunc.0, float* %p0.addr, align 4
-    %p0.ld.0 = load float, float* %p0.addr, align 4
+    store float %fptrunc.0, ptr %p0.addr, align 4
+    %p0.ld.0 = load float, ptr %p0.addr, align 4
     %fpext.0 = fpext float %p0.ld.0 to double
-    store double %fpext.0, double* %p1.addr, align 8
-    %p2.ld.0 = load i32, i32* %p2.addr, align 4
+    store double %fpext.0, ptr %p1.addr, align 8
+    %p2.ld.0 = load i32, ptr %p2.addr, align 4
     %sitof.0 = sitofp i32 %p2.ld.0 to float
-    store float %sitof.0, float* %p0.addr, align 4
-    %p0.ld.1 = load float, float* %p0.addr, align 4
+    store float %sitof.0, ptr %p0.addr, align 4
+    %p0.ld.1 = load float, ptr %p0.addr, align 4
     %ftosi.0 = fptosi float %p0.ld.1 to i32
-    store i32 %ftosi.0, i32* %p2.addr, align 4
-    %p2.ld.1 = load i32, i32* %p2.addr, align 4
+    store i32 %ftosi.0, ptr %p2.addr, align 4
+    %p2.ld.1 = load i32, ptr %p2.addr, align 4
     %sitof.1 = sitofp i32 %p2.ld.1 to double
-    store double %sitof.1, double* %p1.addr, align 8
-    %p1.ld.1 = load double, double* %p1.addr, align 8
+    store double %sitof.1, ptr %p1.addr, align 8
+    %p1.ld.1 = load double, ptr %p1.addr, align 8
     %ftosi.1 = fptosi double %p1.ld.1 to i32
-    store i32 %ftosi.1, i32* %p2.addr, align 4
-    %p3.ld.0 = load i64, i64* %p3.addr, align 8
+    store i32 %ftosi.1, ptr %p2.addr, align 4
+    %p3.ld.0 = load i64, ptr %p3.addr, align 8
     %sitof.2 = sitofp i64 %p3.ld.0 to float
-    store float %sitof.2, float* %p0.addr, align 4
-    %p0.ld.2 = load float, float* %p0.addr, align 4
+    store float %sitof.2, ptr %p0.addr, align 4
+    %p0.ld.2 = load float, ptr %p0.addr, align 4
     %ftosi.2 = fptosi float %p0.ld.2 to i64
-    store i64 %ftosi.2, i64* %p3.addr, align 8
-    %p3.ld.1 = load i64, i64* %p3.addr, align 8
+    store i64 %ftosi.2, ptr %p3.addr, align 8
+    %p3.ld.1 = load i64, ptr %p3.addr, align 8
     %sitof.3 = sitofp i64 %p3.ld.1 to double
-    store double %sitof.3, double* %p1.addr, align 8
-    %p1.ld.2 = load double, double* %p1.addr, align 8
+    store double %sitof.3, ptr %p1.addr, align 8
+    %p1.ld.2 = load double, ptr %p1.addr, align 8
     %ftosi.3 = fptosi double %p1.ld.2 to i64
-    store i64 %ftosi.3, i64* %p3.addr, align 8
-    %p3.ld.2 = load i64, i64* %p3.addr, align 8
+    store i64 %ftosi.3, ptr %p3.addr, align 8
+    %p3.ld.2 = load i64, ptr %p3.addr, align 8
     %trunc.0 = trunc i64 %p3.ld.2 to i32
-    store i32 %trunc.0, i32* %p2.addr, align 4
-    %p2.ld.2 = load i32, i32* %p2.addr, align 4
+    store i32 %trunc.0, ptr %p2.addr, align 4
+    %p2.ld.2 = load i32, ptr %p2.addr, align 4
     %sext.0 = sext i32 %p2.ld.2 to i64
-    store i64 %sext.0, i64* %p3.addr, align 8
-    %p4.ld.0 = load i32, i32* %p4.addr, align 4
+    store i64 %sext.0, ptr %p3.addr, align 8
+    %p4.ld.0 = load i32, ptr %p4.addr, align 4
     %uitof.0 = uitofp i32 %p4.ld.0 to float
-    store float %uitof.0, float* %p0.addr, align 4
-    %p0.ld.3 = load float, float* %p0.addr, align 4
+    store float %uitof.0, ptr %p0.addr, align 4
+    %p0.ld.3 = load float, ptr %p0.addr, align 4
     %ftoui.0 = fptoui float %p0.ld.3 to i32
-    store i32 %ftoui.0, i32* %p4.addr, align 4
-    %p4.ld.1 = load i32, i32* %p4.addr, align 4
+    store i32 %ftoui.0, ptr %p4.addr, align 4
+    %p4.ld.1 = load i32, ptr %p4.addr, align 4
     %uitof.1 = uitofp i32 %p4.ld.1 to double
-    store double %uitof.1, double* %p1.addr, align 8
-    %p1.ld.3 = load double, double* %p1.addr, align 8
+    store double %uitof.1, ptr %p1.addr, align 8
+    %p1.ld.3 = load double, ptr %p1.addr, align 8
     %ftoui.1 = fptoui double %p1.ld.3 to i32
-    store i32 %ftoui.1, i32* %p4.addr, align 4
-    %p4.ld.2 = load i32, i32* %p4.addr, align 4
-    store i32 %p4.ld.2, i32* %p2.addr, align 4
-    %p2.ld.3 = load i32, i32* %p2.addr, align 4
-    store i32 %p2.ld.3, i32* %p4.addr, align 4
-    %p4.ld.3 = load i32, i32* %p4.addr, align 4
+    store i32 %ftoui.1, ptr %p4.addr, align 4
+    %p4.ld.2 = load i32, ptr %p4.addr, align 4
+    store i32 %p4.ld.2, ptr %p2.addr, align 4
+    %p2.ld.3 = load i32, ptr %p2.addr, align 4
+    store i32 %p2.ld.3, ptr %p4.addr, align 4
+    %p4.ld.3 = load i32, ptr %p4.addr, align 4
     %zext.0 = zext i32 %p4.ld.3 to i64
-    store i64 %zext.0, i64* %p3.addr, align 8
-    %p3.ld.3 = load i64, i64* %p3.addr, align 8
+    store i64 %zext.0, ptr %p3.addr, align 8
+    %p3.ld.3 = load i64, ptr %p3.addr, align 8
     %trunc.1 = trunc i64 %p3.ld.3 to i32
-    store i32 %trunc.1, i32* %p4.addr, align 4
-    %p5.ld.0 = load i64, i64* %p5.addr, align 8
+    store i32 %trunc.1, ptr %p4.addr, align 4
+    %p5.ld.0 = load i64, ptr %p5.addr, align 8
     %uitof.2 = uitofp i64 %p5.ld.0 to float
-    store float %uitof.2, float* %p0.addr, align 4
-    %p0.ld.4 = load float, float* %p0.addr, align 4
+    store float %uitof.2, ptr %p0.addr, align 4
+    %p0.ld.4 = load float, ptr %p0.addr, align 4
     %ftoui.2 = fptoui float %p0.ld.4 to i64
-    store i64 %ftoui.2, i64* %p5.addr, align 8
-    %p5.ld.1 = load i64, i64* %p5.addr, align 8
+    store i64 %ftoui.2, ptr %p5.addr, align 8
+    %p5.ld.1 = load i64, ptr %p5.addr, align 8
     %uitof.3 = uitofp i64 %p5.ld.1 to double
-    store double %uitof.3, double* %p1.addr, align 8
-    %p1.ld.4 = load double, double* %p1.addr, align 8
+    store double %uitof.3, ptr %p1.addr, align 8
+    %p1.ld.4 = load double, ptr %p1.addr, align 8
     %ftoui.3 = fptoui double %p1.ld.4 to i64
-    store i64 %ftoui.3, i64* %p5.addr, align 8
-    %p5.ld.2 = load i64, i64* %p5.addr, align 8
+    store i64 %ftoui.3, ptr %p5.addr, align 8
+    %p5.ld.2 = load i64, ptr %p5.addr, align 8
     %trunc.2 = trunc i64 %p5.ld.2 to i32
-    store i32 %trunc.2, i32* %p2.addr, align 4
-    %p2.ld.4 = load i32, i32* %p2.addr, align 4
+    store i32 %trunc.2, ptr %p2.addr, align 4
+    %p2.ld.4 = load i32, ptr %p2.addr, align 4
     %sext.1 = sext i32 %p2.ld.4 to i64
-    store i64 %sext.1, i64* %p5.addr, align 8
-    %p5.ld.3 = load i64, i64* %p5.addr, align 8
-    store i64 %p5.ld.3, i64* %p3.addr, align 8
-    %p3.ld.4 = load i64, i64* %p3.addr, align 8
-    store i64 %p3.ld.4, i64* %p5.addr, align 8
-    %p5.ld.4 = load i64, i64* %p5.addr, align 8
+    store i64 %sext.1, ptr %p5.addr, align 8
+    %p5.ld.3 = load i64, ptr %p5.addr, align 8
+    store i64 %p5.ld.3, ptr %p3.addr, align 8
+    %p3.ld.4 = load i64, ptr %p3.addr, align 8
+    store i64 %p3.ld.4, ptr %p5.addr, align 8
+    %p5.ld.4 = load i64, ptr %p5.addr, align 8
     %trunc.3 = trunc i64 %p5.ld.4 to i32
-    store i32 %trunc.3, i32* %p4.addr, align 4
-    %p4.ld.4 = load i32, i32* %p4.addr, align 4
+    store i32 %trunc.3, ptr %p4.addr, align 4
+    %p4.ld.4 = load i32, ptr %p4.addr, align 4
     %zext.1 = zext i32 %p4.ld.4 to i64
-    store i64 %zext.1, i64* %p5.addr, align 8
+    store i64 %zext.1, ptr %p5.addr, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -493,7 +491,7 @@
   h.mkAssign(xvex4, convex4);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
     %tmp.3 = alloca { double, double }, align 8
     %tmp.2 = alloca { float, float }, align 4
@@ -503,52 +501,36 @@
     %b = alloca { float, float }, align 4
     %x = alloca { double, double }, align 8
     %y = alloca { double, double }, align 8
-    %cast.0 = bitcast { float, float }* %a to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 bitcast ({ float, float }* @const.0 to i8*), i64 8, i1 false)
-    %cast.1 = bitcast { float, float }* %b to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.1, i8* align 4 bitcast ({ float, float }* @const.0 to i8*), i64 8, i1 false)
-    %cast.2 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.2, i8* align 8 bitcast ({ double, double }* @const.1 to i8*), i64 16, i1 false)
-    %cast.3 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.3, i8* align 8 bitcast ({ double, double }* @const.1 to i8*), i64 16, i1 false)
-    %cast.4 = bitcast { double, double }* %tmp.0 to i8*
-    %cast.5 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.4, i8* align 8 %cast.5, i64 16, i1 false)
-    %field.0 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 0
-    %.real.ld.0 = load double, double* %field.0, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 @const.0, i64 8, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b, ptr align 4 @const.0, i64 8, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 @const.1, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 @const.1, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.0, ptr align 8 %x, i64 16, i1 false)
+    %field.0 = getelementptr inbounds { double, double }, ptr %tmp.0, i32 0, i32 0
+    %.real.ld.0 = load double, ptr %field.0, align 8
     %fptrunc.0 = fptrunc double %.real.ld.0 to float
-    %field.1 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 1
-    %.imag.ld.0 = load double, double* %field.1, align 8
+    %field.1 = getelementptr inbounds { double, double }, ptr %tmp.0, i32 0, i32 1
+    %.imag.ld.0 = load double, ptr %field.1, align 8
     %fptrunc.1 = fptrunc double %.imag.ld.0 to float
-    %field.2 = getelementptr inbounds { float, float }, { float, float }* %tmp.1, i32 0, i32 0
-    store float %fptrunc.0, float* %field.2, align 4
-    %field.3 = getelementptr inbounds { float, float }, { float, float }* %tmp.1, i32 0, i32 1
-    store float %fptrunc.1, float* %field.3, align 4
-    %cast.6 = bitcast { float, float }* %a to i8*
-    %cast.7 = bitcast { float, float }* %tmp.1 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.6, i8* align 4 %cast.7, i64 8, i1 false)
-    %cast.8 = bitcast { float, float }* %tmp.2 to i8*
-    %cast.9 = bitcast { float, float }* %b to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.8, i8* align 4 %cast.9, i64 8, i1 false)
-    %field.4 = getelementptr inbounds { float, float }, { float, float }* %tmp.2, i32 0, i32 0
-    %.real.ld.1 = load float, float* %field.4, align 4
+    %field.2 = getelementptr inbounds { float, float }, ptr %tmp.1, i32 0, i32 0
+    store float %fptrunc.0, ptr %field.2, align 4
+    %field.3 = getelementptr inbounds { float, float }, ptr %tmp.1, i32 0, i32 1
+    store float %fptrunc.1, ptr %field.3, align 4
+    call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %tmp.1, i64 8, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp.2, ptr align 4 %b, i64 8, i1 false)
+    %field.4 = getelementptr inbounds { float, float }, ptr %tmp.2, i32 0, i32 0
+    %.real.ld.1 = load float, ptr %field.4, align 4
     %fpext.0 = fpext float %.real.ld.1 to double
-    %field.5 = getelementptr inbounds { float, float }, { float, float }* %tmp.2, i32 0, i32 1
-    %.imag.ld.1 = load float, float* %field.5, align 4
+    %field.5 = getelementptr inbounds { float, float }, ptr %tmp.2, i32 0, i32 1
+    %.imag.ld.1 = load float, ptr %field.5, align 4
     %fpext.1 = fpext float %.imag.ld.1 to double
-    %field.6 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 0
-    store double %fpext.0, double* %field.6, align 8
-    %field.7 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 1
-    store double %fpext.1, double* %field.7, align 8
-    %cast.10 = bitcast { double, double }* %y to i8*
-    %cast.11 = bitcast { double, double }* %tmp.3 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.10, i8* align 8 %cast.11, i64 16, i1 false)
-    %cast.12 = bitcast { float, float }* %a to i8*
-    %cast.13 = bitcast { float, float }* %b to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.12, i8* align 4 %cast.13, i64 8, i1 false)
-    %cast.14 = bitcast { double, double }* %x to i8*
-    %cast.15 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.14, i8* align 8 %cast.15, i64 16, i1 false)
+    %field.6 = getelementptr inbounds { double, double }, ptr %tmp.3, i32 0, i32 0
+    store double %fpext.0, ptr %field.6, align 8
+    %field.7 = getelementptr inbounds { double, double }, ptr %tmp.3, i32 0, i32 1
+    store double %fpext.1, ptr %field.7, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 %tmp.3, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %b, i64 8, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 16, i1 false)
     ret void
   }
   )RAW_RESULT");
@@ -630,61 +612,61 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64 0, i64* %y, align 8
-    store double 0.000000e+00, double* %z, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
+    store i64 0, ptr %x, align 8
+    store i64 0, ptr %y, align 8
+    store double 0.000000e+00, ptr %z, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
     %icmp.0 = icmp eq i64 9, %x.ld.0
     %zext.0 = zext i1 %icmp.0 to i8
-    %x.ld.1 = load i64, i64* %x, align 8
+    %x.ld.1 = load i64, ptr %x, align 8
     %icmp.1 = icmp ne i64 9, %x.ld.1
     %zext.1 = zext i1 %icmp.1 to i8
-    %x.ld.2 = load i64, i64* %x, align 8
+    %x.ld.2 = load i64, ptr %x, align 8
     %icmp.2 = icmp slt i64 9, %x.ld.2
     %zext.2 = zext i1 %icmp.2 to i8
-    %x.ld.3 = load i64, i64* %x, align 8
+    %x.ld.3 = load i64, ptr %x, align 8
     %icmp.3 = icmp sle i64 9, %x.ld.3
     %zext.3 = zext i1 %icmp.3 to i8
-    %x.ld.4 = load i64, i64* %x, align 8
+    %x.ld.4 = load i64, ptr %x, align 8
     %icmp.4 = icmp sgt i64 9, %x.ld.4
     %zext.4 = zext i1 %icmp.4 to i8
-    %x.ld.5 = load i64, i64* %x, align 8
+    %x.ld.5 = load i64, ptr %x, align 8
     %icmp.5 = icmp sge i64 9, %x.ld.5
     %zext.5 = zext i1 %icmp.5 to i8
-    %y.ld.0 = load i64, i64* %y, align 8
+    %y.ld.0 = load i64, ptr %y, align 8
     %icmp.6 = icmp eq i64 9, %y.ld.0
     %zext.6 = zext i1 %icmp.6 to i8
-    %y.ld.1 = load i64, i64* %y, align 8
+    %y.ld.1 = load i64, ptr %y, align 8
     %icmp.7 = icmp ne i64 9, %y.ld.1
     %zext.7 = zext i1 %icmp.7 to i8
-    %y.ld.2 = load i64, i64* %y, align 8
+    %y.ld.2 = load i64, ptr %y, align 8
     %icmp.8 = icmp ult i64 9, %y.ld.2
     %zext.8 = zext i1 %icmp.8 to i8
-    %y.ld.3 = load i64, i64* %y, align 8
+    %y.ld.3 = load i64, ptr %y, align 8
     %icmp.9 = icmp ule i64 9, %y.ld.3
     %zext.9 = zext i1 %icmp.9 to i8
-    %y.ld.4 = load i64, i64* %y, align 8
+    %y.ld.4 = load i64, ptr %y, align 8
     %icmp.10 = icmp ugt i64 9, %y.ld.4
     %zext.10 = zext i1 %icmp.10 to i8
-    %y.ld.5 = load i64, i64* %y, align 8
+    %y.ld.5 = load i64, ptr %y, align 8
     %icmp.11 = icmp uge i64 9, %y.ld.5
     %zext.11 = zext i1 %icmp.11 to i8
-    %z.ld.0 = load double, double* %z, align 8
+    %z.ld.0 = load double, ptr %z, align 8
     %fcmp.0 = fcmp oeq double 9.000000e+00, %z.ld.0
     %zext.12 = zext i1 %fcmp.0 to i8
-    %z.ld.1 = load double, double* %z, align 8
+    %z.ld.1 = load double, ptr %z, align 8
     %fcmp.1 = fcmp une double 9.000000e+00, %z.ld.1
     %zext.13 = zext i1 %fcmp.1 to i8
-    %z.ld.2 = load double, double* %z, align 8
+    %z.ld.2 = load double, ptr %z, align 8
     %fcmp.2 = fcmp olt double 9.000000e+00, %z.ld.2
     %zext.14 = zext i1 %fcmp.2 to i8
-    %z.ld.3 = load double, double* %z, align 8
+    %z.ld.3 = load double, ptr %z, align 8
     %fcmp.3 = fcmp ole double 9.000000e+00, %z.ld.3
     %zext.15 = zext i1 %fcmp.3 to i8
-    %z.ld.4 = load double, double* %z, align 8
+    %z.ld.4 = load double, ptr %z, align 8
     %fcmp.4 = fcmp ogt double 9.000000e+00, %z.ld.4
     %zext.16 = zext i1 %fcmp.4 to i8
-    %z.ld.5 = load double, double* %z, align 8
+    %z.ld.5 = load double, ptr %z, align 8
     %fcmp.5 = fcmp oge double 9.000000e+00, %z.ld.5
     %zext.17 = zext i1 %fcmp.5 to i8
   )RAW_RESULT");
@@ -727,15 +709,15 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store double 0.000000e+00, double* %y, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
+    store i64 0, ptr %x, align 8
+    store double 0.000000e+00, ptr %y, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
     %add.0 = add i64 9, %x.ld.0
-    %x.ld.1 = load i64, i64* %x, align 8
+    %x.ld.1 = load i64, ptr %x, align 8
     %sub.0 = sub i64 9, %x.ld.1
-    %y.ld.0 = load double, double* %y, align 8
+    %y.ld.0 = load double, ptr %y, align 8
     %fadd.0 = fadd double 9.000000e+00, %y.ld.0
-    %y.ld.1 = load double, double* %y, align 8
+    %y.ld.1 = load double, ptr %y, align 8
     %fsub.0 = fsub double 9.000000e+00, %y.ld.1
   )RAW_RESULT");
 
@@ -769,16 +751,16 @@
   h.mkAssign(vex, ypzpw);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64 9, i64* %y, align 8
-    store i64 10, i64* %z, align 8
-    store i64 11, i64* %w, align 8
-    %y.ld.0 = load i64, i64* %y, align 8
-    %z.ld.0 = load i64, i64* %z, align 8
+    store i64 0, ptr %x, align 8
+    store i64 9, ptr %y, align 8
+    store i64 10, ptr %z, align 8
+    store i64 11, ptr %w, align 8
+    %y.ld.0 = load i64, ptr %y, align 8
+    %z.ld.0 = load i64, ptr %z, align 8
     %add.0 = add i64 %y.ld.0, %z.ld.0
-    %w.ld.0 = load i64, i64* %w, align 8
+    %w.ld.0 = load i64, ptr %w, align 8
     %add.1 = add i64 %add.0, %w.ld.0
-    store i64 %add.1, i64* %x, align 8
+    store i64 %add.1, ptr %x, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -825,65 +807,65 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64 0, i64* %y, align 8
-    store i8 0, i8* %z, align 1
-    store i64 0, i64* %x2, align 8
-    store i64 0, i64* %y2, align 8
-    store i8 0, i8* %z2, align 1
-    %x.ld.0 = load i64, i64* %x, align 8
-    %x2.ld.0 = load i64, i64* %x2, align 8
+    store i64 0, ptr %x, align 8
+    store i64 0, ptr %y, align 8
+    store i8 0, ptr %z, align 1
+    store i64 0, ptr %x2, align 8
+    store i64 0, ptr %y2, align 8
+    store i8 0, ptr %z2, align 1
+    %x.ld.0 = load i64, ptr %x, align 8
+    %x2.ld.0 = load i64, ptr %x2, align 8
     %iand.0 = and i64 %x.ld.0, %x2.ld.0
-    %x.ld.1 = load i64, i64* %x, align 8
-    %x2.ld.1 = load i64, i64* %x2, align 8
+    %x.ld.1 = load i64, ptr %x, align 8
+    %x2.ld.1 = load i64, ptr %x2, align 8
     %ior.0 = or i64 %x.ld.1, %x2.ld.1
-    %x.ld.2 = load i64, i64* %x, align 8
-    %x2.ld.2 = load i64, i64* %x2, align 8
+    %x.ld.2 = load i64, ptr %x, align 8
+    %x2.ld.2 = load i64, ptr %x2, align 8
     %iand.1 = and i64 %x.ld.2, %x2.ld.2
-    %x.ld.3 = load i64, i64* %x, align 8
-    %x2.ld.3 = load i64, i64* %x2, align 8
+    %x.ld.3 = load i64, ptr %x, align 8
+    %x2.ld.3 = load i64, ptr %x2, align 8
     %ior.1 = or i64 %x.ld.3, %x2.ld.3
-    %x.ld.4 = load i64, i64* %x, align 8
-    %x2.ld.4 = load i64, i64* %x2, align 8
+    %x.ld.4 = load i64, ptr %x, align 8
+    %x2.ld.4 = load i64, ptr %x2, align 8
     %xor.0 = xor i64 %x.ld.4, %x2.ld.4
-    %x.ld.5 = load i64, i64* %x, align 8
-    %x2.ld.5 = load i64, i64* %x2, align 8
+    %x.ld.5 = load i64, ptr %x, align 8
+    %x2.ld.5 = load i64, ptr %x2, align 8
     %iand.2 = and i64 %x.ld.5, %x2.ld.5
-    %y.ld.0 = load i64, i64* %y, align 8
-    %y2.ld.0 = load i64, i64* %y2, align 8
+    %y.ld.0 = load i64, ptr %y, align 8
+    %y2.ld.0 = load i64, ptr %y2, align 8
     %iand.3 = and i64 %y.ld.0, %y2.ld.0
-    %y.ld.1 = load i64, i64* %y, align 8
-    %y2.ld.1 = load i64, i64* %y2, align 8
+    %y.ld.1 = load i64, ptr %y, align 8
+    %y2.ld.1 = load i64, ptr %y2, align 8
     %ior.2 = or i64 %y.ld.1, %y2.ld.1
-    %y.ld.2 = load i64, i64* %y, align 8
-    %y2.ld.2 = load i64, i64* %y2, align 8
+    %y.ld.2 = load i64, ptr %y, align 8
+    %y2.ld.2 = load i64, ptr %y2, align 8
     %iand.4 = and i64 %y.ld.2, %y2.ld.2
-    %y.ld.3 = load i64, i64* %y, align 8
-    %y2.ld.3 = load i64, i64* %y2, align 8
+    %y.ld.3 = load i64, ptr %y, align 8
+    %y2.ld.3 = load i64, ptr %y2, align 8
     %ior.3 = or i64 %y.ld.3, %y2.ld.3
-    %y.ld.4 = load i64, i64* %y, align 8
-    %y2.ld.4 = load i64, i64* %y2, align 8
+    %y.ld.4 = load i64, ptr %y, align 8
+    %y2.ld.4 = load i64, ptr %y2, align 8
     %xor.1 = xor i64 %y.ld.4, %y2.ld.4
-    %y.ld.5 = load i64, i64* %y, align 8
-    %y2.ld.5 = load i64, i64* %y2, align 8
+    %y.ld.5 = load i64, ptr %y, align 8
+    %y2.ld.5 = load i64, ptr %y2, align 8
     %iand.5 = and i64 %y.ld.5, %y2.ld.5
-    %z.ld.0 = load i8, i8* %z, align 1
-    %z2.ld.0 = load i8, i8* %z2, align 1
+    %z.ld.0 = load i8, ptr %z, align 1
+    %z2.ld.0 = load i8, ptr %z2, align 1
     %iand.6 = and i8 %z.ld.0, %z2.ld.0
-    %z.ld.1 = load i8, i8* %z, align 1
-    %z2.ld.1 = load i8, i8* %z2, align 1
+    %z.ld.1 = load i8, ptr %z, align 1
+    %z2.ld.1 = load i8, ptr %z2, align 1
     %ior.4 = or i8 %z.ld.1, %z2.ld.1
-    %z.ld.2 = load i8, i8* %z, align 1
-    %z2.ld.2 = load i8, i8* %z2, align 1
+    %z.ld.2 = load i8, ptr %z, align 1
+    %z2.ld.2 = load i8, ptr %z2, align 1
     %iand.7 = and i8 %z.ld.2, %z2.ld.2
-    %z.ld.3 = load i8, i8* %z, align 1
-    %z2.ld.3 = load i8, i8* %z2, align 1
+    %z.ld.3 = load i8, ptr %z, align 1
+    %z2.ld.3 = load i8, ptr %z2, align 1
     %ior.5 = or i8 %z.ld.3, %z2.ld.3
-    %z.ld.4 = load i8, i8* %z, align 1
-    %z2.ld.4 = load i8, i8* %z2, align 1
+    %z.ld.4 = load i8, ptr %z, align 1
+    %z2.ld.4 = load i8, ptr %z2, align 1
     %xor.2 = xor i8 %z.ld.4, %z2.ld.4
-    %z.ld.5 = load i8, i8* %z, align 1
-    %z2.ld.5 = load i8, i8* %z2, align 1
+    %z.ld.5 = load i8, ptr %z, align 1
+    %z2.ld.5 = load i8, ptr %z2, align 1
     %iand.8 = and i8 %z.ld.5, %z2.ld.5
   )RAW_RESULT");
 
@@ -931,24 +913,24 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i16 0, i16* %x, align 2
-    store i16 0, i16* %y, align 2
-    store double 0.000000e+00, double* %z, align 8
-    %x.ld.0 = load i16, i16* %x, align 2
+    store i16 0, ptr %x, align 2
+    store i16 0, ptr %y, align 2
+    store double 0.000000e+00, ptr %z, align 8
+    %x.ld.0 = load i16, ptr %x, align 2
     %mul.0 = mul i16 -17, %x.ld.0
-    %x.ld.1 = load i16, i16* %x, align 2
+    %x.ld.1 = load i16, ptr %x, align 2
     %div.0 = sdiv i16 -17, %x.ld.1
-    %x.ld.2 = load i16, i16* %x, align 2
+    %x.ld.2 = load i16, ptr %x, align 2
     %mod.0 = srem i16 -17, %x.ld.2
-    %y.ld.0 = load i16, i16* %y, align 2
+    %y.ld.0 = load i16, ptr %y, align 2
     %mul.1 = mul i16 13, %y.ld.0
-    %y.ld.1 = load i16, i16* %y, align 2
+    %y.ld.1 = load i16, ptr %y, align 2
     %div.1 = udiv i16 13, %y.ld.1
-    %y.ld.2 = load i16, i16* %y, align 2
+    %y.ld.2 = load i16, ptr %y, align 2
     %mod.1 = urem i16 13, %y.ld.2
-    %z.ld.0 = load double, double* %z, align 8
+    %z.ld.0 = load double, ptr %z, align 8
     %fmul.0 = fmul double 9.000000e+00, %z.ld.0
-    %z.ld.1 = load double, double* %z, align 8
+    %z.ld.1 = load double, ptr %z, align 8
     %fdiv.0 = fdiv double 9.000000e+00, %z.ld.1
   )RAW_RESULT");
 
@@ -1011,28 +993,28 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64 0, i64* %y, align 8
-    store i64 0, i64* %s, align 8
-    store i32 0, i32* %z, align 4
-    %x.ld.0 = load i64, i64* %x, align 8
-    %s.ld.0 = load i64, i64* %s, align 8
+    store i64 0, ptr %x, align 8
+    store i64 0, ptr %y, align 8
+    store i64 0, ptr %s, align 8
+    store i32 0, ptr %z, align 4
+    %x.ld.0 = load i64, ptr %x, align 8
+    %s.ld.0 = load i64, ptr %s, align 8
     %shl.0 = shl i64 %x.ld.0, %s.ld.0
-    %x.ld.1 = load i64, i64* %x, align 8
-    %s.ld.1 = load i64, i64* %s, align 8
+    %x.ld.1 = load i64, ptr %x, align 8
+    %s.ld.1 = load i64, ptr %s, align 8
     %shr.0 = ashr i64 %x.ld.1, %s.ld.1
-    %y.ld.0 = load i64, i64* %y, align 8
-    %s.ld.2 = load i64, i64* %s, align 8
+    %y.ld.0 = load i64, ptr %y, align 8
+    %s.ld.2 = load i64, ptr %s, align 8
     %shl.1 = shl i64 %y.ld.0, %s.ld.2
-    %y.ld.1 = load i64, i64* %y, align 8
-    %s.ld.3 = load i64, i64* %s, align 8
+    %y.ld.1 = load i64, ptr %y, align 8
+    %s.ld.3 = load i64, ptr %s, align 8
     %shr.1 = lshr i64 %y.ld.1, %s.ld.3
-    %x.ld.2 = load i64, i64* %x, align 8
-    %z.ld.0 = load i32, i32* %z, align 4
+    %x.ld.2 = load i64, ptr %x, align 8
+    %z.ld.0 = load i32, ptr %z, align 4
     %zext.0 = zext i32 %z.ld.0 to i64
     %shl.2 = shl i64 %x.ld.2, %zext.0
-    %z.ld.1 = load i32, i32* %z, align 4
-    %y.ld.2 = load i64, i64* %y, align 8
+    %z.ld.1 = load i32, ptr %z, align 4
+    %y.ld.2 = load i64, ptr %y, align 8
     %trunc.0 = trunc i64 %y.ld.2 to i32
     %shr.2 = lshr i32 %z.ld.1, %trunc.0
   )RAW_RESULT");
@@ -1074,7 +1056,7 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
     %tmp.12 = alloca { double, double }, align 8
     %tmp.11 = alloca { double, double }, align 8
@@ -1093,134 +1075,105 @@
     %y = alloca { double, double }, align 8
     %z = alloca { double, double }, align 8
     %b = alloca i8, align 1
-    %cast.0 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ double, double }* @const.0 to i8*), i64 16, i1 false)
-    %cast.1 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 bitcast ({ double, double }* @const.0 to i8*), i64 16, i1 false)
-    %cast.2 = bitcast { double, double }* %z to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.2, i8* align 8 bitcast ({ double, double }* @const.0 to i8*), i64 16, i1 false)
-    store i8 0, i8* %b, align 1
-    %cast.3 = bitcast { double, double }* %tmp.0 to i8*
-    %cast.4 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.3, i8* align 8 %cast.4, i64 16, i1 false)
-    %cast.5 = bitcast { double, double }* %tmp.1 to i8*
-    %cast.6 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.5, i8* align 8 %cast.6, i64 16, i1 false)
-    %field.0 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 0
-    %.real.ld.0 = load double, double* %field.0, align 8
-    %field.1 = getelementptr inbounds { double, double }, { double, double }* %tmp.1, i32 0, i32 0
-    %.real.ld.1 = load double, double* %field.1, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 @const.0, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 @const.0, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %z, ptr align 8 @const.0, i64 16, i1 false)
+    store i8 0, ptr %b, align 1
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.0, ptr align 8 %x, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.1, ptr align 8 %y, i64 16, i1 false)
+    %field.0 = getelementptr inbounds { double, double }, ptr %tmp.0, i32 0, i32 0
+    %.real.ld.0 = load double, ptr %field.0, align 8
+    %field.1 = getelementptr inbounds { double, double }, ptr %tmp.1, i32 0, i32 0
+    %.real.ld.1 = load double, ptr %field.1, align 8
     %fadd.0 = fadd double %.real.ld.0, %.real.ld.1
-    %field.2 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 1
-    %.imag.ld.0 = load double, double* %field.2, align 8
-    %field.3 = getelementptr inbounds { double, double }, { double, double }* %tmp.1, i32 0, i32 1
-    %.imag.ld.1 = load double, double* %field.3, align 8
+    %field.2 = getelementptr inbounds { double, double }, ptr %tmp.0, i32 0, i32 1
+    %.imag.ld.0 = load double, ptr %field.2, align 8
+    %field.3 = getelementptr inbounds { double, double }, ptr %tmp.1, i32 0, i32 1
+    %.imag.ld.1 = load double, ptr %field.3, align 8
     %fadd.1 = fadd double %.imag.ld.0, %.imag.ld.1
-    %field.4 = getelementptr inbounds { double, double }, { double, double }* %tmp.2, i32 0, i32 0
-    store double %fadd.0, double* %field.4, align 8
-    %field.5 = getelementptr inbounds { double, double }, { double, double }* %tmp.2, i32 0, i32 1
-    store double %fadd.1, double* %field.5, align 8
-    %cast.7 = bitcast { double, double }* %z to i8*
-    %cast.8 = bitcast { double, double }* %tmp.2 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.7, i8* align 8 %cast.8, i64 16, i1 false)
-    %cast.9 = bitcast { double, double }* %tmp.3 to i8*
-    %cast.10 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.9, i8* align 8 %cast.10, i64 16, i1 false)
-    %cast.11 = bitcast { double, double }* %tmp.4 to i8*
-    %cast.12 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.11, i8* align 8 %cast.12, i64 16, i1 false)
-    %field.6 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 0
-    %.real.ld.2 = load double, double* %field.6, align 8
-    %field.7 = getelementptr inbounds { double, double }, { double, double }* %tmp.4, i32 0, i32 0
-    %.real.ld.3 = load double, double* %field.7, align 8
+    %field.4 = getelementptr inbounds { double, double }, ptr %tmp.2, i32 0, i32 0
+    store double %fadd.0, ptr %field.4, align 8
+    %field.5 = getelementptr inbounds { double, double }, ptr %tmp.2, i32 0, i32 1
+    store double %fadd.1, ptr %field.5, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %z, ptr align 8 %tmp.2, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.3, ptr align 8 %x, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.4, ptr align 8 %y, i64 16, i1 false)
+    %field.6 = getelementptr inbounds { double, double }, ptr %tmp.3, i32 0, i32 0
+    %.real.ld.2 = load double, ptr %field.6, align 8
+    %field.7 = getelementptr inbounds { double, double }, ptr %tmp.4, i32 0, i32 0
+    %.real.ld.3 = load double, ptr %field.7, align 8
     %fsub.0 = fsub double %.real.ld.2, %.real.ld.3
-    %field.8 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 1
-    %.imag.ld.2 = load double, double* %field.8, align 8
-    %field.9 = getelementptr inbounds { double, double }, { double, double }* %tmp.4, i32 0, i32 1
-    %.imag.ld.3 = load double, double* %field.9, align 8
+    %field.8 = getelementptr inbounds { double, double }, ptr %tmp.3, i32 0, i32 1
+    %.imag.ld.2 = load double, ptr %field.8, align 8
+    %field.9 = getelementptr inbounds { double, double }, ptr %tmp.4, i32 0, i32 1
+    %.imag.ld.3 = load double, ptr %field.9, align 8
     %fsub.1 = fsub double %.imag.ld.2, %.imag.ld.3
-    %field.10 = getelementptr inbounds { double, double }, { double, double }* %tmp.5, i32 0, i32 0
-    store double %fsub.0, double* %field.10, align 8
-    %field.11 = getelementptr inbounds { double, double }, { double, double }* %tmp.5, i32 0, i32 1
-    store double %fsub.1, double* %field.11, align 8
-    %cast.13 = bitcast { double, double }* %z to i8*
-    %cast.14 = bitcast { double, double }* %tmp.5 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.13, i8* align 8 %cast.14, i64 16, i1 false)
-    %cast.15 = bitcast { double, double }* %tmp.6 to i8*
-    %cast.16 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.15, i8* align 8 %cast.16, i64 16, i1 false)
-    %cast.17 = bitcast { double, double }* %tmp.7 to i8*
-    %cast.18 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.17, i8* align 8 %cast.18, i64 16, i1 false)
-    %field.12 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 0
-    %.real.ld.4 = load double, double* %field.12, align 8
-    %field.13 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 0
-    %.real.ld.5 = load double, double* %field.13, align 8
+    %field.10 = getelementptr inbounds { double, double }, ptr %tmp.5, i32 0, i32 0
+    store double %fsub.0, ptr %field.10, align 8
+    %field.11 = getelementptr inbounds { double, double }, ptr %tmp.5, i32 0, i32 1
+    store double %fsub.1, ptr %field.11, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %z, ptr align 8 %tmp.5, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.6, ptr align 8 %x, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.7, ptr align 8 %y, i64 16, i1 false)
+    %field.12 = getelementptr inbounds { double, double }, ptr %tmp.6, i32 0, i32 0
+    %.real.ld.4 = load double, ptr %field.12, align 8
+    %field.13 = getelementptr inbounds { double, double }, ptr %tmp.7, i32 0, i32 0
+    %.real.ld.5 = load double, ptr %field.13, align 8
     %fmul.0 = fmul double %.real.ld.4, %.real.ld.5
-    %field.14 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 1
-    %.imag.ld.4 = load double, double* %field.14, align 8
-    %field.15 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 1
-    %.imag.ld.5 = load double, double* %field.15, align 8
+    %field.14 = getelementptr inbounds { double, double }, ptr %tmp.6, i32 0, i32 1
+    %.imag.ld.4 = load double, ptr %field.14, align 8
+    %field.15 = getelementptr inbounds { double, double }, ptr %tmp.7, i32 0, i32 1
+    %.imag.ld.5 = load double, ptr %field.15, align 8
     %fmul.1 = fmul double %.imag.ld.4, %.imag.ld.5
     %fsub.2 = fsub double %fmul.0, %fmul.1
-    %field.16 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 0
-    %.field.ld.0 = load double, double* %field.16, align 8
-    %field.17 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 1
-    %.field.ld.1 = load double, double* %field.17, align 8
+    %field.16 = getelementptr inbounds { double, double }, ptr %tmp.6, i32 0, i32 0
+    %.field.ld.0 = load double, ptr %field.16, align 8
+    %field.17 = getelementptr inbounds { double, double }, ptr %tmp.7, i32 0, i32 1
+    %.field.ld.1 = load double, ptr %field.17, align 8
     %fmul.2 = fmul double %.field.ld.0, %.field.ld.1
-    %field.18 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 1
-    %.field.ld.2 = load double, double* %field.18, align 8
-    %field.19 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 0
-    %.field.ld.3 = load double, double* %field.19, align 8
+    %field.18 = getelementptr inbounds { double, double }, ptr %tmp.6, i32 0, i32 1
+    %.field.ld.2 = load double, ptr %field.18, align 8
+    %field.19 = getelementptr inbounds { double, double }, ptr %tmp.7, i32 0, i32 0
+    %.field.ld.3 = load double, ptr %field.19, align 8
     %fmul.3 = fmul double %.field.ld.2, %.field.ld.3
     %fadd.2 = fadd double %fmul.2, %fmul.3
-    %field.20 = getelementptr inbounds { double, double }, { double, double }* %tmp.8, i32 0, i32 0
-    store double %fsub.2, double* %field.20, align 8
-    %field.21 = getelementptr inbounds { double, double }, { double, double }* %tmp.8, i32 0, i32 1
-    store double %fadd.2, double* %field.21, align 8
-    %cast.19 = bitcast { double, double }* %z to i8*
-    %cast.20 = bitcast { double, double }* %tmp.8 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.19, i8* align 8 %cast.20, i64 16, i1 false)
-    %cast.21 = bitcast { double, double }* %tmp.9 to i8*
-    %cast.22 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.21, i8* align 8 %cast.22, i64 16, i1 false)
-    %cast.23 = bitcast { double, double }* %tmp.10 to i8*
-    %cast.24 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.23, i8* align 8 %cast.24, i64 16, i1 false)
-    %field.22 = getelementptr inbounds { double, double }, { double, double }* %tmp.9, i32 0, i32 0
-    %.real.ld.6 = load double, double* %field.22, align 8
-    %field.23 = getelementptr inbounds { double, double }, { double, double }* %tmp.10, i32 0, i32 0
-    %.real.ld.7 = load double, double* %field.23, align 8
+    %field.20 = getelementptr inbounds { double, double }, ptr %tmp.8, i32 0, i32 0
+    store double %fsub.2, ptr %field.20, align 8
+    %field.21 = getelementptr inbounds { double, double }, ptr %tmp.8, i32 0, i32 1
+    store double %fadd.2, ptr %field.21, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %z, ptr align 8 %tmp.8, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.9, ptr align 8 %x, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.10, ptr align 8 %y, i64 16, i1 false)
+    %field.22 = getelementptr inbounds { double, double }, ptr %tmp.9, i32 0, i32 0
+    %.real.ld.6 = load double, ptr %field.22, align 8
+    %field.23 = getelementptr inbounds { double, double }, ptr %tmp.10, i32 0, i32 0
+    %.real.ld.7 = load double, ptr %field.23, align 8
     %fcmp.0 = fcmp oeq double %.real.ld.6, %.real.ld.7
     %zext.0 = zext i1 %fcmp.0 to i8
-    %field.24 = getelementptr inbounds { double, double }, { double, double }* %tmp.9, i32 0, i32 1
-    %.imag.ld.6 = load double, double* %field.24, align 8
-    %field.25 = getelementptr inbounds { double, double }, { double, double }* %tmp.10, i32 0, i32 1
-    %.imag.ld.7 = load double, double* %field.25, align 8
+    %field.24 = getelementptr inbounds { double, double }, ptr %tmp.9, i32 0, i32 1
+    %.imag.ld.6 = load double, ptr %field.24, align 8
+    %field.25 = getelementptr inbounds { double, double }, ptr %tmp.10, i32 0, i32 1
+    %.imag.ld.7 = load double, ptr %field.25, align 8
     %fcmp.1 = fcmp oeq double %.imag.ld.6, %.imag.ld.7
     %zext.1 = zext i1 %fcmp.1 to i8
     %iand.0 = and i8 %zext.0, %zext.1
-    store i8 %iand.0, i8* %b, align 1
-    %cast.25 = bitcast { double, double }* %tmp.11 to i8*
-    %cast.26 = bitcast { double, double }* %x to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.25, i8* align 8 %cast.26, i64 16, i1 false)
-    %cast.27 = bitcast { double, double }* %tmp.12 to i8*
-    %cast.28 = bitcast { double, double }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.27, i8* align 8 %cast.28, i64 16, i1 false)
-    %field.26 = getelementptr inbounds { double, double }, { double, double }* %tmp.11, i32 0, i32 0
-    %.real.ld.8 = load double, double* %field.26, align 8
-    %field.27 = getelementptr inbounds { double, double }, { double, double }* %tmp.12, i32 0, i32 0
-    %.real.ld.9 = load double, double* %field.27, align 8
+    store i8 %iand.0, ptr %b, align 1
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.11, ptr align 8 %x, i64 16, i1 false)
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp.12, ptr align 8 %y, i64 16, i1 false)
+    %field.26 = getelementptr inbounds { double, double }, ptr %tmp.11, i32 0, i32 0
+    %.real.ld.8 = load double, ptr %field.26, align 8
+    %field.27 = getelementptr inbounds { double, double }, ptr %tmp.12, i32 0, i32 0
+    %.real.ld.9 = load double, ptr %field.27, align 8
     %fcmp.2 = fcmp une double %.real.ld.8, %.real.ld.9
     %zext.2 = zext i1 %fcmp.2 to i8
-    %field.28 = getelementptr inbounds { double, double }, { double, double }* %tmp.11, i32 0, i32 1
-    %.imag.ld.8 = load double, double* %field.28, align 8
-    %field.29 = getelementptr inbounds { double, double }, { double, double }* %tmp.12, i32 0, i32 1
-    %.imag.ld.9 = load double, double* %field.29, align 8
+    %field.28 = getelementptr inbounds { double, double }, ptr %tmp.11, i32 0, i32 1
+    %.imag.ld.8 = load double, ptr %field.28, align 8
+    %field.29 = getelementptr inbounds { double, double }, ptr %tmp.12, i32 0, i32 1
+    %.imag.ld.9 = load double, ptr %field.29, align 8
     %fcmp.3 = fcmp une double %.imag.ld.8, %.imag.ld.9
     %zext.3 = zext i1 %fcmp.3 to i8
     %ior.0 = or i8 %zext.2, %zext.3
-    store i8 %ior.0, i8* %b, align 1
+    store i8 %ior.0, ptr %b, align 1
     ret void
   }
   )RAW_RESULT");
@@ -1266,22 +1219,21 @@
   h.mkAssign(xvex3, compex);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store double 0.000000e+00, double* %a, align 8
-    store double 0.000000e+00, double* %b, align 8
-    %cast.0 = bitcast { double, double }* %x to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ double, double }* @const.0 to i8*), i64 16, i1 false)
-    %field.0 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 0
-    %x.real.ld.0 = load double, double* %field.0, align 8
-    store double %x.real.ld.0, double* %a, align 8
-    %field.1 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 1
-    %x.imag.ld.0 = load double, double* %field.1, align 8
-    store double %x.imag.ld.0, double* %b, align 8
-    %b.ld.0 = load double, double* %b, align 8
-    %a.ld.0 = load double, double* %a, align 8
-    %field.2 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 0
-    store double %b.ld.0, double* %field.2, align 8
-    %field.3 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 1
-    store double %a.ld.0, double* %field.3, align 8
+    store double 0.000000e+00, ptr %a, align 8
+    store double 0.000000e+00, ptr %b, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 @const.0, i64 16, i1 false)
+    %field.0 = getelementptr inbounds { double, double }, ptr %x, i32 0, i32 0
+    %x.real.ld.0 = load double, ptr %field.0, align 8
+    store double %x.real.ld.0, ptr %a, align 8
+    %field.1 = getelementptr inbounds { double, double }, ptr %x, i32 0, i32 1
+    %x.imag.ld.0 = load double, ptr %field.1, align 8
+    store double %x.imag.ld.0, ptr %b, align 8
+    %b.ld.0 = load double, ptr %b, align 8
+    %a.ld.0 = load double, ptr %a, align 8
+    %field.2 = getelementptr inbounds { double, double }, ptr %x, i32 0, i32 0
+    store double %b.ld.0, ptr %field.2, align 8
+    %field.3 = getelementptr inbounds { double, double }, ptr %x, i32 0, i32 1
+    store double %a.ld.0, ptr %field.3, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -1299,7 +1251,7 @@
   {
     Bexpression *snil = be->string_constant_expression("");
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-        i8* null
+        ptr null
       )RAW_RESULT");
     bool isOK = h.expectValue(snil->value(), exp);
     EXPECT_TRUE(isOK && "Value does not have expected contents");
@@ -1308,7 +1260,7 @@
   {
     Bexpression *sblah = be->string_constant_expression("blah");
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-      i8* getelementptr inbounds ([5 x i8], [5 x i8]* @const.0, i32 0, i32 0)
+      @const.0 = private constant [5 x i8] c"blah\00", align 1
     )RAW_RESULT");
     bool isOK = h.expectValue(sblah->value(), exp);
     EXPECT_TRUE(isOK && "Value does not have expected contents");
@@ -1342,28 +1294,28 @@
   h.mkExprStmt(condex);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
     %a = alloca i64, align 8
     %b = alloca i64, align 8
-    store i64 0, i64* %a, align 8
-    store i64 0, i64* %b, align 8
-    %a.ld.0 = load i64, i64* %a, align 8
-    %b.ld.0 = load i64, i64* %b, align 8
+    store i64 0, ptr %a, align 8
+    store i64 0, ptr %b, align 8
+    %a.ld.0 = load i64, ptr %a, align 8
+    %b.ld.0 = load i64, ptr %b, align 8
     %icmp.0 = icmp slt i64 %a.ld.0, %b.ld.0
     %zext.0 = zext i1 %icmp.0 to i8
     %trunc.0 = trunc i8 %zext.0 to i1
     br i1 %trunc.0, label %then.0, label %else.0
-  
+
   then.0:                                           ; preds = %entry
-    call void @foo(i8* nest undef)
+    call void @foo(ptr nest undef)
     br label %fallthrough.0
-  
+
   fallthrough.0:                                    ; preds = %else.0, %then.0
     ret void
-  
+
   else.0:                                           ; preds = %entry
-    call void @foo(i8* nest undef)
+    call void @foo(ptr nest undef)
     br label %fallthrough.0
   }
   )RAW_RESULT");
@@ -1399,24 +1351,24 @@
   h.mkExprStmt(condex);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
+  define void @foo(ptr nest %nest.0) #0 {
   entry:
     %a = alloca i64, align 8
     %tmpv.0 = alloca i64, align 8
-    store i64 0, i64* %a, align 8
+    store i64 0, ptr %a, align 8
     br i1 true, label %then.0, label %else.0
-  
+
   then.0:                                           ; preds = %entry
-    call void @foo(i8* nest undef)
+    call void @foo(ptr nest undef)
     br label %fallthrough.0
-  
+
   fallthrough.0:                                    ; preds = %else.0, %then.0
-    %tmpv.0.ld.0 = load i64, i64* %tmpv.0, align 8
+    %tmpv.0.ld.0 = load i64, ptr %tmpv.0, align 8
     ret void
-  
+
   else.0:                                           ; preds = %entry
-    %a.ld.0 = load i64, i64* %a, align 8
-    store i64 %a.ld.0, i64* %tmpv.0, align 8
+    %a.ld.0 = load i64, ptr %a, align 8
+    store i64 %a.ld.0, ptr %tmpv.0, align 8
     br label %fallthrough.0
   }
   )RAW_RESULT");
@@ -1455,35 +1407,30 @@
   h.mkLocal("a", s2t, cond);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo({ [16 x i32], i32 }* sret({ [16 x i32], i32 }) %sret.formal.0, i8* nest %nest.0, { [16 x i32], i32 }* byval({ [16 x i32], i32 }) %p0, i32 %p1) #0 {
-  entry:
-    %p1.addr = alloca i32, align 4
-    %a = alloca { [16 x i32], i32 }, align 4
-    %tmpv.0 = alloca { [16 x i32], i32 }, align 4
-    store i32 %p1, i32* %p1.addr, align 4
-    %p1.ld.0 = load i32, i32* %p1.addr, align 4
-    %icmp.0 = icmp slt i32 %p1.ld.0, 7
-    %zext.0 = zext i1 %icmp.0 to i8
-    %trunc.0 = trunc i8 %zext.0 to i1
-    br i1 %trunc.0, label %then.0, label %else.0
-  
-  then.0:                                           ; preds = %entry
-    %cast.0 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    %cast.1 = bitcast { [16 x i32], i32 }* %p0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 %cast.1, i64 68, i1 false)
-    br label %fallthrough.0
-  
-  fallthrough.0:                                    ; preds = %else.0, %then.0
-    %cast.3 = bitcast { [16 x i32], i32 }* %a to i8*
-    %cast.4 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 68, i1 false)
-    ret void
-  
-  else.0:                                           ; preds = %entry
-    %cast.2 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.2, i8* align 4 bitcast ({ [16 x i32], i32 }* @const.0 to i8*), i64 68, i1 false)
-    br label %fallthrough.0
-  }
+    define void @foo(ptr sret({ [16 x i32], i32 }) %sret.formal.0, ptr nest %nest.0, ptr byval({ [16 x i32], i32 }) %p0, i32 %p1) #0 {
+    entry:
+      %p1.addr = alloca i32, align 4
+      %a = alloca { [16 x i32], i32 }, align 4
+      %tmpv.0 = alloca { [16 x i32], i32 }, align 4
+      store i32 %p1, ptr %p1.addr, align 4
+      %p1.ld.0 = load i32, ptr %p1.addr, align 4
+      %icmp.0 = icmp slt i32 %p1.ld.0, 7
+      %zext.0 = zext i1 %icmp.0 to i8
+      %trunc.0 = trunc i8 %zext.0 to i1
+      br i1 %trunc.0, label %then.0, label %else.0
+
+    then.0:                                           ; preds = %entry
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmpv.0, ptr align 4 %p0, i64 68, i1 false)
+      br label %fallthrough.0
+
+    fallthrough.0:                                    ; preds = %else.0, %then.0
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %tmpv.0, i64 68, i1 false)
+      ret void
+
+    else.0:                                           ; preds = %entry
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmpv.0, ptr align 4 @const.0, i64 68, i1 false)
+      br label %fallthrough.0
+    }
   )RAW_RESULT");
 
   bool broken = h.finish(StripDebugInfo);
@@ -1530,35 +1477,30 @@
   h.mkLocal("a", s2t, cond);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo({ [16 x i32], i32 }* sret({ [16 x i32], i32 }) %sret.formal.0, i8* nest %nest.0, { [16 x i32], i32 }* %p0, i32 %p1) #0 {
-  entry:
-    %p1.addr = alloca i32, align 4
-    %a = alloca { [16 x i32], i32 }, align 4
-    %tmpv.0 = alloca { [16 x i32], i32 }, align 4
-    store i32 %p1, i32* %p1.addr, align 4
-    %p1.ld.0 = load i32, i32* %p1.addr, align 4
-    %icmp.0 = icmp slt i32 %p1.ld.0, 7
-    %zext.0 = zext i1 %icmp.0 to i8
-    %trunc.0 = trunc i8 %zext.0 to i1
-    br i1 %trunc.0, label %then.0, label %else.0
-  
-  then.0:                                           ; preds = %entry
-    %cast.0 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    %cast.1 = bitcast { [16 x i32], i32 }* %p0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 %cast.1, i64 68, i1 false)
-    br label %fallthrough.0
-  
-  fallthrough.0:                                    ; preds = %else.0, %then.0
-    %cast.3 = bitcast { [16 x i32], i32 }* %a to i8*
-    %cast.4 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 68, i1 false)
-    ret void
-  
-  else.0:                                           ; preds = %entry
-    %cast.2 = bitcast { [16 x i32], i32 }* %tmpv.0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.2, i8* align 4 bitcast ({ [16 x i32], i32 }* @const.0 to i8*), i64 68, i1 false)
-    br label %fallthrough.0
-  }
+    define void @foo(ptr sret({ [16 x i32], i32 }) %sret.formal.0, ptr nest %nest.0, ptr %p0, i32 %p1) #0 {
+    entry:
+      %p1.addr = alloca i32, align 4
+      %a = alloca { [16 x i32], i32 }, align 4
+      %tmpv.0 = alloca { [16 x i32], i32 }, align 4
+      store i32 %p1, ptr %p1.addr, align 4
+      %p1.ld.0 = load i32, ptr %p1.addr, align 4
+      %icmp.0 = icmp slt i32 %p1.ld.0, 7
+      %zext.0 = zext i1 %icmp.0 to i8
+      %trunc.0 = trunc i8 %zext.0 to i1
+      br i1 %trunc.0, label %then.0, label %else.0
+
+    then.0:                                           ; preds = %entry
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmpv.0, ptr align 4 %p0, i64 68, i1 false)
+      br label %fallthrough.0
+
+    fallthrough.0:                                    ; preds = %else.0, %then.0
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %tmpv.0, i64 68, i1 false)
+      ret void
+
+    else.0:                                           ; preds = %entry
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmpv.0, ptr align 4 @const.0, i64 68, i1 false)
+      br label %fallthrough.0
+    }
   )RAW_RESULT");
 
   bool broken = h.finish(StripDebugInfo);
@@ -1589,18 +1531,18 @@
   h.addStmt(es);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 {
+  define i64 @foo(ptr nest %nest.0, i32 %param1, i32 %param2, ptr %param3) #0 {
   entry:
     %param1.addr = alloca i32, align 4
     %param2.addr = alloca i32, align 4
-    %param3.addr = alloca i64*, align 8
+    %param3.addr = alloca ptr, align 8
     %x = alloca i64, align 8
-    store i32 %param1, i32* %param1.addr, align 4
-    store i32 %param2, i32* %param2.addr, align 4
-    store i64* %param3, i64** %param3.addr, align 8
-    store i64 0, i64* %x, align 8
-    store i64 5, i64* %x, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
+    store i32 %param1, ptr %param1.addr, align 4
+    store i32 %param2, ptr %param2.addr, align 4
+    store ptr %param3, ptr %param3.addr, align 8
+    store i64 0, ptr %x, align 8
+    store i64 5, ptr %x, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
     ret i64 0
   }
   )RAW_RESULT");
@@ -1639,30 +1581,27 @@
   h.addStmt(st2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 {
+  define i64 @foo(ptr nest %nest.0, i32 %param1, i32 %param2, ptr %param3) #0 {
   entry:
     %tmp.0 = alloca { i64, i64 }, align 8
     %param1.addr = alloca i32, align 4
     %param2.addr = alloca i32, align 4
-    %param3.addr = alloca i64*, align 8
+    %param3.addr = alloca ptr, align 8
     %x = alloca i64, align 8
     %y = alloca { i64, i64 }, align 8
-    store i32 %param1, i32* %param1.addr, align 4
-    store i32 %param2, i32* %param2.addr, align 4
-    store i64* %param3, i64** %param3.addr, align 8
-    store i64 0, i64* %x, align 8
-    %cast.0 = bitcast { i64, i64 }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i64, i64 }* @const.0 to i8*), i64 16, i1 false)
-    store i64 5, i64* %x, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
-    %x.ld.1 = load i64, i64* %x, align 8
-    %field.0 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp.0, i32 0, i32 0
-    store i64 %x.ld.0, i64* %field.0, align 8
-    %field.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp.0, i32 0, i32 1
-    store i64 %x.ld.1, i64* %field.1, align 8
-    %cast.1 = bitcast { i64, i64 }* %y to i8*
-    %cast.2 = bitcast { i64, i64 }* %tmp.0 to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 %cast.2, i64 16, i1 false)
+    store i32 %param1, ptr %param1.addr, align 4
+    store i32 %param2, ptr %param2.addr, align 4
+    store ptr %param3, ptr %param3.addr, align 8
+    store i64 0, ptr %x, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 @const.0, i64 16, i1 false)
+    store i64 5, ptr %x, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
+    %x.ld.1 = load i64, ptr %x, align 8
+    %field.0 = getelementptr inbounds { i64, i64 }, ptr %tmp.0, i32 0, i32 0
+    store i64 %x.ld.0, ptr %field.0, align 8
+    %field.1 = getelementptr inbounds { i64, i64 }, ptr %tmp.0, i32 0, i32 1
+    store i64 %x.ld.1, ptr %field.1, align 8
+    call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 %tmp.0, i64 16, i1 false)
     ret i64 0
   }
   )RAW_RESULT");
@@ -1701,32 +1640,32 @@
   h.mkAssign(dex, mkInt32Const(be, 7));
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0, i32* %p0, i32* %p1) #0 {
+  define void @foo(ptr nest %nest.0, ptr %p0, ptr %p1) #0 {
   entry:
-    %p0.addr = alloca i32*, align 8
-    %p1.addr = alloca i32*, align 8
-    %tmpv.0 = alloca i32*, align 8
-    store i32* %p0, i32** %p0.addr, align 8
-    store i32* %p1, i32** %p1.addr, align 8
-    %p0.ld.0 = load i32*, i32** %p0.addr, align 8
-    %icmp.0 = icmp eq i32* %p0.ld.0, null
+    %p0.addr = alloca ptr, align 8
+    %p1.addr = alloca ptr, align 8
+    %tmpv.0 = alloca ptr, align 8
+    store ptr %p0, ptr %p0.addr, align 8
+    store ptr %p1, ptr %p1.addr, align 8
+    %p0.ld.0 = load ptr, ptr %p0.addr, align 8
+    %icmp.0 = icmp eq ptr %p0.ld.0, null
     %zext.0 = zext i1 %icmp.0 to i8
     %trunc.0 = trunc i8 %zext.0 to i1
     br i1 %trunc.0, label %then.0, label %else.0
-  
+
   then.0:                                           ; preds = %entry
-    %p1.ld.0 = load i32*, i32** %p1.addr, align 8
-    store i32* %p1.ld.0, i32** %tmpv.0, align 8
+    %p1.ld.0 = load ptr, ptr %p1.addr, align 8
+    store ptr %p1.ld.0, ptr %tmpv.0, align 8
     br label %fallthrough.0
-  
+
   fallthrough.0:                                    ; preds = %else.0, %then.0
-    %tmpv.0.ld.0 = load i32*, i32** %tmpv.0, align 8
-    store i32 7, i32* %tmpv.0.ld.0, align 4
+    %tmpv.0.ld.0 = load ptr, ptr %tmpv.0, align 8
+    store i32 7, ptr %tmpv.0.ld.0, align 4
     ret void
-  
+
   else.0:                                           ; preds = %entry
-    %p0.ld.1 = load i32*, i32** %p0.addr, align 8
-    store i32* %p0.ld.1, i32** %tmpv.0, align 8
+    %p0.ld.1 = load ptr, ptr %p0.addr, align 8
+    store ptr %p0.ld.1, ptr %tmpv.0, align 8
     br label %fallthrough.0
   }
   )RAW_RESULT");
@@ -1774,24 +1713,24 @@
   h.mkLocal("r", bf64t, be->unary_expression(OPERATOR_MINUS, veq, loc));
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i8 0, i8* %x, align 1
-    %x.ld.0 = load i8, i8* %x, align 1
+    store i8 0, ptr %x, align 1
+    %x.ld.0 = load i8, ptr %x, align 1
     %icmp.0 = icmp ne i8 %x.ld.0, 0
     %xor.0 = xor i1 %icmp.0, true
     %zext.0 = zext i1 %xor.0 to i8
-    store i8 %zext.0, i8* %y, align 1
-    store i32 0, i32* %a, align 4
-    %a.ld.0 = load i32, i32* %a, align 4
+    store i8 %zext.0, ptr %y, align 1
+    store i32 0, ptr %a, align 4
+    %a.ld.0 = load i32, ptr %a, align 4
     %sub.0 = sub i32 0, %a.ld.0
-    store i32 %sub.0, i32* %b, align 4
-    store i64 0, i64* %z, align 8
-    %z.ld.0 = load i64, i64* %z, align 8
+    store i32 %sub.0, ptr %b, align 4
+    store i64 0, ptr %z, align 8
+    %z.ld.0 = load i64, ptr %z, align 8
     %xor.1 = xor i64 %z.ld.0, -1
-    store i64 %xor.1, i64* %w, align 8
-    store double 0.000000e+00, double* %q, align 8
-    %q.ld.0 = load double, double* %q, align 8
+    store i64 %xor.1, ptr %w, align 8
+    store double 0.000000e+00, ptr %q, align 8
+    %q.ld.0 = load double, ptr %q, align 8
     %fsub.0 = fsub double -0.000000e+00, %q.ld.0
-    store double %fsub.0, double* %r, align 8
+    store double %fsub.0, ptr %r, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -1821,7 +1760,7 @@
   h.mkExprStmt(call1);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    call addrspace(0) void @foo(i8* nest undef, i8* null, i32* null, i64* null)
+    call addrspace(0) void @foo(ptr nest undef, ptr null, ptr null, ptr null)
   )RAW_RESULT");
 
   // Note that this
diff --git a/unittests/BackendCore/BackendFcnTests.cpp b/unittests/BackendCore/BackendFcnTests.cpp
index 2af5163..82a0d6f 100644
--- a/unittests/BackendCore/BackendFcnTests.cpp
+++ b/unittests/BackendCore/BackendFcnTests.cpp
@@ -298,8 +298,8 @@
   h.mkExprStmt(call);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
+    store i64 0, ptr %x, align 8
+    %x.ld.0 = load i64, ptr %x, align 8
     %call.0 = call addrspace(0) i64 @llvm.cttz.i64(i64 %x.ld.0, i1 true)
   )RAW_RESULT");
 
@@ -364,17 +364,11 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64 10101, i64* %y, align 8
-    %cast.0 = bitcast i64* %x to i8*
-    %cast.1 = bitcast i64* %y to i8*
-    %call.0 = call addrspace(0) i32 @memcmp(i8* %cast.0, i8* %cast.1, i64 8)
-    %cast.2 = bitcast i64* %x to i8*
-    %cast.3 = bitcast i64* %y to i8*
-    call addrspace(0) void @llvm.memmove.p0i8.p0i8.i64(i8* %cast.2, i8* %cast.3, i64 8, i1 false)
-    %cast.4 = bitcast i64* %y to i8*
-    %cast.5 = bitcast i64* %x to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.4, i8* %cast.5, i64 8, i1 false)
+    store i64 0, ptr %x, align 8
+    store i64 10101, ptr %y, align 8
+    %call.0 = call addrspace(0) i32 @memcmp(ptr %x, ptr %y, i64 8)
+    call addrspace(0) void @llvm.memmove.p0.p0.i64(ptr %x, ptr %y, i64 8, i1 false)
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr %y, ptr %x, i64 8, i1 false)
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -425,10 +419,10 @@
   h.mkLocal("y", bi32t, call32);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %call.0 = call addrspace(0) i64 @syscall(i8* nest undef, i64 64)
-    store i64 %call.0, i64* %x, align 8
-    %call.1 = call addrspace(0) i32 bitcast (i64 (i8*, i64)* @syscall to i32 (i8*, i32)*)(i8* nest undef, i32 32)
-    store i32 %call.1, i32* %y, align 4
+    %call.0 = call addrspace(0) i64 @syscall(ptr nest undef, i64 64)
+    store i64 %call.0, ptr %x, align 8
+    %call.1 = call addrspace(0) i32 @syscall(ptr nest undef, i32 32)
+    store i32 %call.1, ptr %y, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -498,14 +492,14 @@
   h.mkLocal("y", bps1t, call4);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %call.0 = call addrspace(0) i32 @bar(i8* nest undef)
-    store i32 %call.0, i32* %a, align 4
-    %call.1 = call addrspace(0) i32 @bar(i8* nest undef)
-    store i32 %call.1, i32* %b, align 4
-    %call.2 = call addrspace(0) {}* bitcast ({ i32 }* (i8*)* @baz to {}* (i8*)*)(i8* nest undef)
-    store {}* %call.2, {}** %x, align 8
-    %call.3 = call addrspace(0) { i32 }* @baz(i8* nest undef)
-    store { i32 }* %call.3, { i32 }** %y, align 8
+    %call.0 = call addrspace(0) i32 @bar(ptr nest undef)
+    store i32 %call.0, ptr %a, align 4
+    %call.1 = call addrspace(0) i32 @bar(ptr nest undef)
+    store i32 %call.1, ptr %b, align 4
+    %call.2 = call addrspace(0) ptr @baz(ptr nest undef)
+    store ptr %call.2, ptr %x, align 8
+    %call.3 = call addrspace(0) ptr @baz(ptr nest undef)
+    store ptr %call.3, ptr %y, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
diff --git a/unittests/BackendCore/BackendNodeTests.cpp b/unittests/BackendCore/BackendNodeTests.cpp
index 13a3a5a..9a9396c 100644
--- a/unittests/BackendCore/BackendNodeTests.cpp
+++ b/unittests/BackendCore/BackendNodeTests.cpp
@@ -92,36 +92,36 @@
   EXPECT_EQ(res1, matsub);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    node - pre 18
-    pre child + 18 15
-    node + pre 15
-    pre child const 15 1
+    node - pre 17
+    pre child + 17 14
+    node + pre 14
+    pre child const 14 1
     node const pre 1
     node const post 1
-    post child const 15 1
-    pre child deref 15 14
-    node deref pre 14
-    pre child var 14 8
+    post child const 14 1
+    pre child deref 14 13
+    node deref pre 13
+    pre child var 13 8
     node var pre 8
     node var post 8
-    post child var 14 8
-    node deref post 14
-    post child deref 15 14
-    node + post 15
-    post child + 18 15
-    pre child deref 18 17
-    node deref pre 17
+    post child var 13 8
+    node deref post 13
+    post child deref 14 13
+    node + post 14
+    post child + 17 14
     pre child deref 17 16
     node deref pre 16
-    pre child var 16 10
+    pre child deref 16 15
+    node deref pre 15
+    pre child var 15 10
     node var pre 10
     node var post 10
-    post child var 16 10
+    post child var 15 10
+    node deref post 15
+    post child deref 16 15
     node deref post 16
     post child deref 17 16
-    node deref post 17
-    post child deref 18 17
-    node - post 18
+    node - post 17
   )RAW_RESULT");
 
   std::string reason;
@@ -163,12 +163,12 @@
   EXPECT_NE(add, matclone);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %x.ld.0 = load i32, i32* %x, align 4
-    %z.ld.0 = load i16, i16* %z, align 2
+    %x.ld.0 = load i32, ptr %x, align 4
+    %z.ld.0 = load i16, ptr %z, align 2
     %sext.0 = sext i16 %z.ld.0 to i32
     %add.0 = add i32 %x.ld.0, %sext.0
-    %y.ld.0 = load i32*, i32** %y, align 8
-    %.ld.0 = load i32, i32* %y.ld.0, align 4
+    %y.ld.0 = load ptr, ptr %y, align 8
+    %.ld.0 = load i32, ptr %y.ld.0, align 4
     %add.1 = add i32 %add.0, %.ld.0
   )RAW_RESULT");
 
@@ -205,14 +205,14 @@
   Bexpression *matadd = be->materialize(add);
 
   DECLARE_EXPECTED_OUTPUT(exp2, R"RAW_RESULT(
-    %field.0 = getelementptr inbounds { { i32*, i32 }, { i32*, i32 } }, { { i32*, i32 }, { i32*, i32 } }* %x, i32 0, i32 0
-    %field.1 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %field.0, i32 0, i32 0
-    %x.field.field.ld.0 = load i32*, i32** %field.1, align 8
-    %.ld.0 = load i32, i32* %x.field.field.ld.0, align 4
-    %field.2 = getelementptr inbounds { { i32*, i32 }, { i32*, i32 } }, { { i32*, i32 }, { i32*, i32 } }* %x, i32 0, i32 0
-    %field.3 = getelementptr inbounds { i32*, i32 }, { i32*, i32 }* %field.2, i32 0, i32 0
-    %.field.field.ld.0 = load i32*, i32** %field.3, align 8
-    %.ld.1 = load i32, i32* %.field.field.ld.0, align 4
+    %field.0 = getelementptr inbounds { { ptr, i32 }, { ptr, i32 } }, ptr %x, i32 0, i32 0
+    %field.1 = getelementptr inbounds { ptr, i32 }, ptr %field.0, i32 0, i32 0
+    %x.field.field.ld.0 = load ptr, ptr %field.1, align 8
+    %.ld.0 = load i32, ptr %x.field.field.ld.0, align 4
+    %field.2 = getelementptr inbounds { { ptr, i32 }, { ptr, i32 } }, ptr %x, i32 0, i32 0
+    %field.3 = getelementptr inbounds { ptr, i32 }, ptr %field.2, i32 0, i32 0
+    %.field.field.ld.0 = load ptr, ptr %field.3, align 8
+    %.ld.1 = load i32, ptr %.field.field.ld.0, align 4
     %add.0 = add i32 %.ld.0, %.ld.1
   )RAW_RESULT");
 
diff --git a/unittests/BackendCore/BackendPointerExprTests.cpp b/unittests/BackendCore/BackendPointerExprTests.cpp
index f5882ca..0de8d66 100644
--- a/unittests/BackendCore/BackendPointerExprTests.cpp
+++ b/unittests/BackendCore/BackendPointerExprTests.cpp
@@ -72,14 +72,14 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 10, i64* %y, align 8
-    store i64* null, i64** %x, align 8
-    store i64* %y, i64** %x, align 8
-    %x.ld.0 = load i64*, i64** %x, align 8
-    %.ld.0 = load i64, i64* %x.ld.0, align 8
-    store i64 %.ld.0, i64* %y, align 8
-    %x.ld.1 = load i64*, i64** %x, align 8
-    store i64 3, i64* %x.ld.1, align 8
+    store i64 10, ptr %y, align 8
+    store ptr null, ptr %x, align 8
+    store ptr %y, ptr %x, align 8
+    %x.ld.0 = load ptr, ptr %x, align 8
+    %.ld.0 = load i64, ptr %x.ld.0, align 8
+    store i64 %.ld.0, ptr %y, align 8
+    %x.ld.1 = load ptr, ptr %x, align 8
+    store i64 3, ptr %x.ld.1, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -130,16 +130,13 @@
   h.mkAssign(vex3, rvex3);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %cast.0 = bitcast { i64 }* %fdloc1 to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.0, i8* align 8 bitcast ({ i64 }* @const.0 to i8*), i64 8, i1 false)
-    store { i64 }* %fdloc1, { i64 }** %fploc1, align 8
-    store { i64 (i8*, i32, i32, i64*)* }* null, { i64 (i8*, i32, i32, i64*)* }** %fploc2, align 8
-    %fploc1.ld.0 = load { i64 }*, { i64 }** %fploc1, align 8
-    %cast.1 = bitcast { i64 }* %fploc1.ld.0 to { i64 (i8*, i32, i32, i64*)* }*
-    store { i64 (i8*, i32, i32, i64*)* }* %cast.1, { i64 (i8*, i32, i32, i64*)* }** %fploc2, align 8
-    %fploc2.ld.0 = load { i64 (i8*, i32, i32, i64*)* }*, { i64 (i8*, i32, i32, i64*)* }** %fploc2, align 8
-    %cast.2 = bitcast { i64 (i8*, i32, i32, i64*)* }* %fploc2.ld.0 to { i64 }*
-    store { i64 }* %cast.2, { i64 }** %fploc1, align 8
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 8 %fdloc1, ptr align 8 @const.0, i64 8, i1 false)
+    store ptr %fdloc1, ptr %fploc1, align 8
+    store ptr null, ptr %fploc2, align 8
+    %fploc1.ld.0 = load ptr, ptr %fploc1, align 8
+    store ptr %fploc1.ld.0, ptr %fploc2, align 8
+    %fploc2.ld.0 = load ptr, ptr %fploc2, align 8
+    store ptr %fploc2.ld.0, ptr %fploc1, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -157,7 +154,7 @@
   // Manufacture a nil pointer expression
   Bexpression *npe = be->nil_pointer_expression();
   DECLARE_EXPECTED_OUTPUT(exp1, R"RAW_RESULT(
-    i64* null
+    ptr null
   )RAW_RESULT");
   bool isOK = h.expectValue(npe->value(), exp1);
   EXPECT_TRUE(isOK && "Value does not have expected contents");
@@ -188,16 +185,16 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp2, R"RAW_RESULT(
-    store i8 0, i8* %b1, align 1
-    store i8* null, i8** %pb1, align 8
-    %pb1.ld.0 = load i8*, i8** %pb1, align 8
-    %icmp.0 = icmp eq i8* %pb1.ld.0, null
+    store i8 0, ptr %b1, align 1
+    store ptr null, ptr %pb1, align 8
+    %pb1.ld.0 = load ptr, ptr %pb1, align 8
+    %icmp.0 = icmp eq ptr %pb1.ld.0, null
     %zext.0 = zext i1 %icmp.0 to i8
-    store i8 %zext.0, i8* %b1, align 1
-    %pb1.ld.1 = load i8*, i8** %pb1, align 8
-    %icmp.1 = icmp eq i8* null, %pb1.ld.1
+    store i8 %zext.0, ptr %b1, align 1
+    %pb1.ld.1 = load ptr, ptr %pb1, align 8
+    %icmp.1 = icmp eq ptr null, %pb1.ld.1
     %zext.1 = zext i1 %icmp.1 to i8
-    store i8 %zext.1, i8* %b1, align 1
+    store i8 %zext.1, ptr %b1, align 1
   )RAW_RESULT");
 
   bool isOK2 = h.expectBlock(exp2);
@@ -225,10 +222,9 @@
   h.mkLocal("y", bst, deref2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %deref.ld.0 = load i32, i32* null, align 4
-    store i32 %deref.ld.0, i32* %x, align 4
-    %cast.2 = bitcast { i32, i32 }* %y to i8*
-    call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.2, i8* align 4 null, i64 8, i1 false)
+    %deref.ld.0 = load i32, ptr null, align 4
+    store i32 %deref.ld.0, ptr %x, align 4
+    call addrspace(0) void @llvm.memcpy.p0.p0.i64(ptr align 4 %y, ptr align 4 null, i64 8, i1 false)
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -321,38 +317,31 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store %CPT.0* null, %CPT.0** %cpv1, align 8
-    store %CPT.0* null, %CPT.0** %cpv2, align 8
-    %cast.0 = bitcast %CPT.0** %cpv2 to %CPT.0*
-    store %CPT.0* %cast.0, %CPT.0** %cpv1, align 8
-    %cast.1 = bitcast %CPT.0** %cpv1 to %CPT.0*
-    store %CPT.0* %cast.1, %CPT.0** %cpv2, align 8
-    store i8 0, i8* %b1, align 1
-    store i8 0, i8* %b2, align 1
-    store i8 0, i8* %b3, align 1
-    %cpv1.ld.0 = load %CPT.0*, %CPT.0** %cpv1, align 8
-    %cast.2 = bitcast %CPT.0** %cpv2 to %CPT.0***
-    %cpv2.ld.0 = load %CPT.0**, %CPT.0*** %cast.2, align 8
-    %.ld.0 = load %CPT.0*, %CPT.0** %cpv2.ld.0, align 8
-    %icmp.0 = icmp eq %CPT.0* %cpv1.ld.0, %.ld.0
+    store ptr null, ptr %cpv1, align 8
+    store ptr null, ptr %cpv2, align 8
+    store ptr %cpv2, ptr %cpv1, align 8
+    store ptr %cpv1, ptr %cpv2, align 8
+    store i8 0, ptr %b1, align 1
+    store i8 0, ptr %b2, align 1
+    store i8 0, ptr %b3, align 1
+    %cpv1.ld.0 = load ptr, ptr %cpv1, align 8
+    %cpv2.ld.0 = load ptr, ptr %cpv2, align 8
+    %.ld.0 = load ptr, ptr %cpv2.ld.0, align 8
+    %icmp.0 = icmp eq ptr %cpv1.ld.0, %.ld.0
     %zext.0 = zext i1 %icmp.0 to i8
-    store i8 %zext.0, i8* %b1, align 1
-    %cpv2.ld.1 = load %CPT.0*, %CPT.0** %cpv2, align 8
-    %cast.3 = bitcast %CPT.0* %cpv2.ld.1 to %CPT.0**
-    %icmp.1 = icmp eq %CPT.0** %cpv1, %cast.3
+    store i8 %zext.0, ptr %b1, align 1
+    %cpv2.ld.1 = load ptr, ptr %cpv2, align 8
+    %icmp.1 = icmp eq ptr %cpv1, %cpv2.ld.1
     %zext.1 = zext i1 %icmp.1 to i8
-    store i8 %zext.1, i8* %b2, align 1
-    %cpv1.ld.1 = load %CPT.0*, %CPT.0** %cpv1, align 8
-    %cast.4 = bitcast %CPT.0** %cpv2 to %CPT.0***
-    %cpv2.ld.2 = load %CPT.0**, %CPT.0*** %cast.4, align 8
-    %cast.5 = bitcast %CPT.0** %cpv2.ld.2 to %CPT.0***
-    %deref.ld.0 = load %CPT.0**, %CPT.0*** %cast.5, align 8
-    %cast.6 = bitcast %CPT.0** %deref.ld.0 to %CPT.0***
-    %deref.ld.1 = load %CPT.0**, %CPT.0*** %cast.6, align 8
-    %.ld.1 = load %CPT.0*, %CPT.0** %deref.ld.1, align 8
-    %icmp.2 = icmp eq %CPT.0* %cpv1.ld.1, %.ld.1
+    store i8 %zext.1, ptr %b2, align 1
+    %cpv1.ld.1 = load ptr, ptr %cpv1, align 8
+    %cpv2.ld.2 = load ptr, ptr %cpv2, align 8
+    %deref.ld.0 = load ptr, ptr %cpv2.ld.2, align 8
+    %deref.ld.1 = load ptr, ptr %deref.ld.0, align 8
+    %.ld.1 = load ptr, ptr %deref.ld.1, align 8
+    %icmp.2 = icmp eq ptr %cpv1.ld.1, %.ld.1
     %zext.2 = zext i1 %icmp.2 to i8
-    store i8 %zext.2, i8* %b3, align 1
+    store i8 %zext.2, ptr %b3, align 1
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -422,18 +411,17 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store %CPT.0* null, %CPT.0** %x, align 8
-    store %CPT.0** null, %CPT.0*** %y, align 8
-    %cast.0 = bitcast %CPT.0*** %y to %CPT.0*
-    store %CPT.0* %cast.0, %CPT.0** %x, align 8
-    store %CPT.0** %x, %CPT.0*** %y, align 8
-    store i8 0, i8* %b1, align 1
-    %x.ld.0 = load %CPT.0*, %CPT.0** %x, align 8
-    %y.ld.0 = load %CPT.0**, %CPT.0*** %y, align 8
-    %.ld.0 = load %CPT.0*, %CPT.0** %y.ld.0, align 8
-    %icmp.0 = icmp eq %CPT.0* %x.ld.0, %.ld.0
+    store ptr null, ptr %x, align 8
+    store ptr null, ptr %y, align 8
+    store ptr %y, ptr %x, align 8
+    store ptr %x, ptr %y, align 8
+    store i8 0, ptr %b1, align 1
+    %x.ld.0 = load ptr, ptr %x, align 8
+    %y.ld.0 = load ptr, ptr %y, align 8
+    %.ld.0 = load ptr, ptr %y.ld.0, align 8
+    %icmp.0 = icmp eq ptr %x.ld.0, %.ld.0
     %zext.0 = zext i1 %icmp.0 to i8
-    store i8 %zext.0, i8* %b1, align 1
+    store i8 %zext.0, ptr %b1, align 1
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -477,14 +465,14 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %param3.ld.0 = load i64*, i64** %param3.addr, align 8
-    %ptroff.0 = getelementptr i64, i64* %param3.ld.0, i32 5
-    store i64 9, i64* %ptroff.0, align 8
-    %param3.ld.1 = load i64*, i64** %param3.addr, align 8
-    %ptroff.1 = getelementptr i64, i64* %param3.ld.1, i32 7
-    %.ptroff.ld.0 = load i64, i64* %ptroff.1, align 8
+    %param3.ld.0 = load ptr, ptr %param3.addr, align 8
+    %ptroff.0 = getelementptr i64, ptr %param3.ld.0, i32 5
+    store i64 9, ptr %ptroff.0, align 8
+    %param3.ld.1 = load ptr, ptr %param3.addr, align 8
+    %ptroff.1 = getelementptr i64, ptr %param3.ld.1, i32 7
+    %.ptroff.ld.0 = load i64, ptr %ptroff.1, align 8
     %trunc.0 = trunc i64 %.ptroff.ld.0 to i32
-    store i32 %trunc.0, i32* %param1.addr, align 4
+    store i32 %trunc.0, ptr %param1.addr, align 4
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -516,8 +504,8 @@
   h.mkAssign(vexl, ad3);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %x, align 8
-    store i64* %x, i64** %param3.addr, align 8
+    store i64 0, ptr %x, align 8
+    store ptr %x, ptr %param3.addr, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -569,8 +557,8 @@
   }
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i32 0, i32* inttoptr (i64 65793 to i32*), align 4
-    store i64 2, i64* getelementptr inbounds ({ i64, i64 }, { i64, i64 }* inttoptr (i64 34661 to { i64, i64 }*), i32 0, i32 1), align 8
+    store i32 0, ptr inttoptr (i64 65793 to ptr), align 4
+    store i64 2, ptr getelementptr inbounds ({ i64, i64 }, ptr inttoptr (i64 34661 to ptr), i32 0, i32 1), align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
@@ -622,9 +610,8 @@
   h.mkLocal("y", pbefty2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 (i8*, i64, i64, %CFT.0*, %CFT.1*)* null, i64 (i8*, i64, i64, %CFT.0*, %CFT.1*)** %x, align 8
-    store i64 (i8*, i64, i64, %CFT.1*, %CFT.0*)* null, i64 (i8*, i64, i64, %CFT.1*, %CFT.0*)** %y, align 8
-
+    store ptr null, ptr %x, align 8
+    store ptr null, ptr %y, align 8
   )RAW_RESULT");
 
   bool isOK = h.expectBlock(exp);
diff --git a/unittests/BackendCore/BackendStmtTests.cpp b/unittests/BackendCore/BackendStmtTests.cpp
index 7e47928..c2661a9 100644
--- a/unittests/BackendCore/BackendStmtTests.cpp
+++ b/unittests/BackendCore/BackendStmtTests.cpp
@@ -40,7 +40,7 @@
   Bstatement *is = be->init_statement(func, loc1, mkInt64Const(be, 10));
   ASSERT_TRUE(is != nullptr);
   h.addStmt(is);
-  EXPECT_EQ(repr(is), "store i64 10, i64* %loc1, align 8");
+  EXPECT_EQ(repr(is), "store i64 10, ptr %loc1, align 8");
 
   // error handling
   Bvariable *loc2 = be->local_variable(func, "loc2", bi64t, nullptr, true, loc);
@@ -80,11 +80,11 @@
   h.addStmt(as2);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    store i64 0, i64* %loc1, align 8
-    store i64 123, i64* %loc1, align 8
-    store i64 0, i64* %loc2, align 8
-    %loc1.ld.0 = load i64, i64* %loc1, align 8
-    store i64 %loc1.ld.0, i64* %loc2, align 8
+    store i64 0, ptr %loc1, align 8
+    store i64 123, ptr %loc1, align 8
+    store i64 0, ptr %loc2, align 8
+    %loc1.ld.0 = load i64, ptr %loc1, align 8
+    store i64 %loc1.ld.0, ptr %loc2, align 8
   )RAW_RESULT");
   bool isOK = h.expectBlock(exp);
   EXPECT_TRUE(isOK && "Block does not have expected contents");
@@ -116,7 +116,7 @@
   Bstatement *ret = h.mkReturn(ve1);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    %loc1.ld.0 = load i64, i64* %loc1, align 8
+    %loc1.ld.0 = load i64, ptr %loc1, align 8
     ret i64 %loc1.ld.0
   )RAW_RESULT");
   std::string reason;
@@ -157,19 +157,19 @@
   h.mkReturn(addexpr);
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 {
-  entry:
-    %param1.addr = alloca i32, align 4
-    %param2.addr = alloca i32, align 4
-    %param3.addr = alloca i64*, align 8
-    %x = alloca i64, align 8
-    store i32 %param1, i32* %param1.addr, align 4
-    store i32 %param2, i32* %param2.addr, align 4
-    store i64* %param3, i64** %param3.addr, align 8
-    store i64 10, i64* %x, align 8
-    %x.ld.0 = load i64, i64* %x, align 8
-    ret i64 %x.ld.0
-  }
+    define i64 @foo(ptr nest %nest.0, i32 %param1, i32 %param2, ptr %param3) #0 {
+    entry:
+      %param1.addr = alloca i32, align 4
+      %param2.addr = alloca i32, align 4
+      %param3.addr = alloca ptr, align 8
+      %x = alloca i64, align 8
+      store i32 %param1, ptr %param1.addr, align 4
+      store i32 %param2, ptr %param2.addr, align 4
+      store ptr %param3, ptr %param3.addr, align 8
+      store i64 10, ptr %x, align 8
+      %x.ld.0 = load i64, ptr %x, align 8
+     ret i64 %x.ld.0
+    }
   )RAW_RESULT");
 
   bool broken = h.finish(StripDebugInfo);
@@ -240,17 +240,17 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
-  entry:
-    %loc1 = alloca i8, align 1
-    store i8 0, i8* %loc1, align 1
-    call void @bar(i8* nest undef, i8* blockaddress(@foo, %label.0))
-    br label %label.0
-  
-  label.0:                                          ; preds = %entry
-    store i8 0, i8* %loc1, align 1
-    ret void
-  }
+    define void @foo(ptr nest %nest.0) #0 {
+    entry:
+      %loc1 = alloca i8, align 1
+      store i8 0, ptr %loc1, align 1
+      call void @bar(ptr nest undef, ptr blockaddress(@foo, %label.0))
+      br label %label.0
+
+    label.0:                                          ; preds = %entry
+      store i8 0, ptr %loc1, align 1
+      ret void
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -311,41 +311,41 @@
 
   // verify
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 {
-  entry:
-    %param1.addr = alloca i32, align 4
-    %param2.addr = alloca i32, align 4
-    %param3.addr = alloca i64*, align 8
-    %loc1 = alloca i64, align 8
-    %loc2 = alloca i64, align 8
-    store i32 %param1, i32* %param1.addr, align 4
-    store i32 %param2, i32* %param2.addr, align 4
-    store i64* %param3, i64** %param3.addr, align 8
-    store i64 0, i64* %loc1, align 8
-    store i64 0, i64* %loc2, align 8
-    br i1 true, label %then.0, label %else.0
-  
-  then.0:                                           ; preds = %entry
-    br i1 true, label %then.1, label %else.1
-  
-  fallthrough.0:                                    ; preds = %else.0, %fallthrough.1
-    ret i64 10101
-  
-  else.0:                                           ; preds = %entry
-    store i64 456, i64* %loc2, align 8
-    br label %fallthrough.0
-  
-  then.1:                                           ; preds = %then.0
-    store i64 123, i64* %loc1, align 8
-    br label %fallthrough.1
-  
-  fallthrough.1:                                    ; preds = %else.1, %then.1
-    br label %fallthrough.0
-  
-  else.1:                                           ; preds = %then.0
-    store i64 987, i64* %loc1, align 8
-    br label %fallthrough.1
-  }
+    define i64 @foo(ptr nest %nest.0, i32 %param1, i32 %param2, ptr %param3) #0 {
+    entry:
+      %param1.addr = alloca i32, align 4
+      %param2.addr = alloca i32, align 4
+      %param3.addr = alloca ptr, align 8
+      %loc1 = alloca i64, align 8
+      %loc2 = alloca i64, align 8
+      store i32 %param1, ptr %param1.addr, align 4
+      store i32 %param2, ptr %param2.addr, align 4
+      store ptr %param3, ptr %param3.addr, align 8
+      store i64 0, ptr %loc1, align 8
+      store i64 0, ptr %loc2, align 8
+      br i1 true, label %then.0, label %else.0
+
+    then.0:                                           ; preds = %entry
+      br i1 true, label %then.1, label %else.1
+
+    fallthrough.0:                                    ; preds = %else.0, %fallthrough.1
+      ret i64 10101
+
+    else.0:                                           ; preds = %entry
+      store i64 456, ptr %loc2, align 8
+      br label %fallthrough.0
+
+    then.1:                                           ; preds = %then.0
+      store i64 123, ptr %loc1, align 8
+      br label %fallthrough.1
+
+    fallthrough.1:                                    ; preds = %else.1, %then.1
+      br label %fallthrough.0
+
+    else.1:                                           ; preds = %then.0
+      store i64 987, ptr %loc1, align 8
+      br label %fallthrough.1
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -435,65 +435,65 @@
 
   // verify
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 {
-  entry:
-    %param1.addr = alloca i32, align 4
-    %param2.addr = alloca i32, align 4
-    %param3.addr = alloca i64*, align 8
-    %loc1 = alloca i64, align 8
-    %tmpv.0 = alloca i64, align 8
-    store i32 %param1, i32* %param1.addr, align 4
-    store i32 %param2, i32* %param2.addr, align 4
-    store i64* %param3, i64** %param3.addr, align 8
-    store i64 0, i64* %loc1, align 8
-    %loc1.ld.4 = load i64, i64* %loc1, align 8
-    switch i64 %loc1.ld.4, label %default.0 [
-      i64 1, label %case.0
-      i64 2, label %case.0
-      i64 3, label %case.1
-      i64 4, label %case.1
-      i64 5, label %case.2
-    ]
-  
-  case.0:                                           ; preds = %entry, %entry
-    %loc1.ld.0 = load i64, i64* %loc1, align 8
-    %div.0 = sdiv i64 %loc1.ld.0, 123
-    store i64 %div.0, i64* %loc1, align 8
-    br label %label.0
-  
-  case.1:                                           ; preds = %entry, %entry
-    %loc1.ld.1 = load i64, i64* %loc1, align 8
-    %icmp.0 = icmp sle i64 %loc1.ld.1, 987
-    %zext.0 = zext i1 %icmp.0 to i8
-    %trunc.0 = trunc i8 %zext.0 to i1
-    br i1 %trunc.0, label %then.0, label %else.0
-  
-  case.2:                                           ; preds = %entry, %fallthrough.0
-    br label %default.0
-  
-  default.0:                                        ; preds = %entry, %case.2
-    store i64 456, i64* %loc1, align 8
-    br label %label.0
-  
-  label.0:                                          ; preds = %default.0, %case.0
-    ret i64 10101
-  
-  then.0:                                           ; preds = %case.1
-    %loc1.ld.3 = load i64, i64* %loc1, align 8
-    store i64 %loc1.ld.3, i64* %tmpv.0, align 8
-    br label %fallthrough.0
-  
-  fallthrough.0:                                    ; preds = %else.0, %then.0
-    %tmpv.0.ld.0 = load i64, i64* %tmpv.0, align 8
-    store i64 %tmpv.0.ld.0, i64* %loc1, align 8
-    br label %case.2
-  
-  else.0:                                           ; preds = %case.1
-    %loc1.ld.2 = load i64, i64* %loc1, align 8
-    %mul.0 = mul i64 987, %loc1.ld.2
-    store i64 %mul.0, i64* %tmpv.0, align 8
-    br label %fallthrough.0
-  }
+    define i64 @foo(ptr nest %nest.0, i32 %param1, i32 %param2, ptr %param3) #0 {
+    entry:
+      %param1.addr = alloca i32, align 4
+      %param2.addr = alloca i32, align 4
+      %param3.addr = alloca ptr, align 8
+      %loc1 = alloca i64, align 8
+      %tmpv.0 = alloca i64, align 8
+      store i32 %param1, ptr %param1.addr, align 4
+      store i32 %param2, ptr %param2.addr, align 4
+      store ptr %param3, ptr %param3.addr, align 8
+      store i64 0, ptr %loc1, align 8
+      %loc1.ld.4 = load i64, ptr %loc1, align 8
+      switch i64 %loc1.ld.4, label %default.0 [
+        i64 1, label %case.0
+        i64 2, label %case.0
+        i64 3, label %case.1
+        i64 4, label %case.1
+        i64 5, label %case.2
+      ]
+
+    case.0:                                           ; preds = %entry, %entry
+      %loc1.ld.0 = load i64, ptr %loc1, align 8
+      %div.0 = sdiv i64 %loc1.ld.0, 123
+      store i64 %div.0, ptr %loc1, align 8
+      br label %label.0
+
+    case.1:                                           ; preds = %entry, %entry
+      %loc1.ld.1 = load i64, ptr %loc1, align 8
+      %icmp.0 = icmp sle i64 %loc1.ld.1, 987
+      %zext.0 = zext i1 %icmp.0 to i8
+      %trunc.0 = trunc i8 %zext.0 to i1
+      br i1 %trunc.0, label %then.0, label %else.0
+
+    case.2:                                           ; preds = %entry, %fallthrough.0
+      br label %default.0
+
+    default.0:                                        ; preds = %entry, %case.2
+      store i64 456, ptr %loc1, align 8
+      br label %label.0
+
+    label.0:                                          ; preds = %default.0, %case.0
+      ret i64 10101
+
+    then.0:                                           ; preds = %case.1
+      %loc1.ld.3 = load i64, ptr %loc1, align 8
+      store i64 %loc1.ld.3, ptr %tmpv.0, align 8
+      br label %fallthrough.0
+
+    fallthrough.0:                                    ; preds = %else.0, %then.0
+      %tmpv.0.ld.0 = load i64, ptr %tmpv.0, align 8
+      store i64 %tmpv.0.ld.0, ptr %loc1, align 8
+      br label %case.2
+
+    else.0:                                           ; preds = %case.1
+      %loc1.ld.2 = load i64, ptr %loc1, align 8
+      %mul.0 = mul i64 987, %loc1.ld.2
+      store i64 %mul.0, ptr %tmpv.0, align 8
+      br label %fallthrough.0
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -584,28 +584,28 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 {
-  entry:
-    %x = alloca i8, align 1
-    store i8 0, i8* %x, align 1
-    br label %finish.0
-  
-  pad.0:                                            ; preds = %finish.0
-    %ex.0 = landingpad { i8*, i32 }
-            catch i8* null
-    br label %catch.0
-  
-  catch.0:                                          ; preds = %pad.0
-    call void @checkdefer(i8* nest undef, i8* %x)
-    br label %finish.0
-  
-  finish.0:                                         ; preds = %catch.0, %entry
-    invoke void @deferreturn(i8* nest undef, i8* %x)
-            to label %cont.0 unwind label %pad.0
-  
-  cont.0:                                           ; preds = %finish.0
-    ret void
-  }
+    define void @foo(ptr nest %nest.0) #0 personality ptr @__gccgo_personality_v0 {
+    entry:
+      %x = alloca i8, align 1
+      store i8 0, ptr %x, align 1
+      br label %finish.0
+
+    pad.0:                                            ; preds = %finish.0
+      %ex.0 = landingpad { ptr, i32 }
+              catch ptr null
+      br label %catch.0
+
+    catch.0:                                          ; preds = %pad.0
+      call void @checkdefer(ptr nest undef, ptr %x)
+      br label %finish.0
+
+    finish.0:                                         ; preds = %catch.0, %entry
+      invoke void @deferreturn(ptr nest undef, ptr %x)
+              to label %cont.0 unwind label %pad.0
+
+    cont.0:                                           ; preds = %finish.0
+      ret void
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -697,94 +697,94 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @baz(i8* nest %nest.0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 {
-  entry:
-    %ehtmp.0 = alloca { i8*, i32 }, align 8
-    %x = alloca i64, align 8
-    %y = alloca i8, align 1
-    %sret.actual.0 = alloca { i8, i8 }, align 1
-    %sret.actual.1 = alloca { i8, i8 }, align 1
-    %finvar.0 = alloca i8, align 1
-    store i64 0, i64* %x, align 8
-    store i8 0, i8* %y, align 1
-    %call.0 = invoke i64 @id(i8* nest undef, i64 99)
-            to label %cont.1 unwind label %pad.1
-  
-  finok.0:                                          ; preds = %cont.4
-    store i8 1, i8* %finvar.0, align 1
-    br label %finally.0
-  
-  finally.0:                                        ; preds = %catchpad.0, %finok.0
-    br label %finish.0
-  
-  pad.0:                                            ; preds = %fallthrough.0, %finish.0
-    %ex.0 = landingpad { i8*, i32 }
-            catch i8* null
-    br label %catch.0
-  
-  catch.0:                                          ; preds = %pad.0
-    call void @checkdefer(i8* nest undef, i8* %y)
-    br label %finish.0
-  
-  finish.0:                                         ; preds = %catch.0, %finally.0
-    invoke void @deferreturn(i8* nest undef, i8* %y)
-            to label %cont.0 unwind label %pad.0
-  
-  cont.0:                                           ; preds = %fallthrough.0, %finish.0
-    %fload.0 = load i8, i8* %finvar.0, align 1
-    %icmp.0 = icmp eq i8 %fload.0, 1
-    br i1 %icmp.0, label %finret.0, label %finres.0
-  
-  pad.1:                                            ; preds = %then.0, %cont.1, %entry
-    %ex.1 = landingpad { i8*, i32 }
-            catch i8* null
-    br label %catch.1
-  
-  catch.1:                                          ; preds = %pad.1
-    invoke void @plix(i8* nest undef)
-            to label %cont.4 unwind label %catchpad.0
-  
-  catchpad.0:                                       ; preds = %catch.1
-    %ex2.0 = landingpad { i8*, i32 }
-            cleanup
-    store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0, align 8
-    store i8 0, i8* %finvar.0, align 1
-    br label %finally.0
-  
-  cont.1:                                           ; preds = %entry
-    store i64 %call.0, i64* %x, align 8
-    invoke void @plark(i8* nest undef)
-            to label %cont.2 unwind label %pad.1
-  
-  cont.2:                                           ; preds = %cont.1
-    br i1 false, label %then.0, label %else.0
-  
-  then.0:                                           ; preds = %cont.2
-    %call.1 = invoke i16 @noret(i8* nest undef)
-            to label %cont.3 unwind label %pad.1
-  
-  fallthrough.0:                                    ; preds = %else.0
-    store i64 123, i64* %x, align 8
-    store i8 1, i8* %finvar.0, align 1
-    invoke void @deferreturn(i8* nest undef, i8* %y)
-            to label %cont.0 unwind label %pad.0
-  
-  else.0:                                           ; preds = %cont.2
-    br label %fallthrough.0
-  
-  cont.3:                                           ; preds = %then.0
-    unreachable
-  
-  cont.4:                                           ; preds = %catch.1
-    br label %finok.0
-  
-  finres.0:                                         ; preds = %cont.0
-    %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0, align 8
-    resume { i8*, i32 } %excv.0
-  
-  finret.0:                                         ; preds = %cont.0
-    ret void
-  }
+    define void @baz(ptr nest %nest.0) #0 personality ptr @__gccgo_personality_v0 {
+    entry:
+      %ehtmp.0 = alloca { ptr, i32 }, align 8
+      %x = alloca i64, align 8
+      %y = alloca i8, align 1
+      %sret.actual.0 = alloca { i8, i8 }, align 1
+      %sret.actual.1 = alloca { i8, i8 }, align 1
+      %finvar.0 = alloca i8, align 1
+      store i64 0, ptr %x, align 8
+      store i8 0, ptr %y, align 1
+      %call.0 = invoke i64 @id(ptr nest undef, i64 99)
+              to label %cont.1 unwind label %pad.1
+
+    finok.0:                                          ; preds = %cont.4
+      store i8 1, ptr %finvar.0, align 1
+      br label %finally.0
+
+    finally.0:                                        ; preds = %catchpad.0, %finok.0
+      br label %finish.0
+
+    pad.0:                                            ; preds = %fallthrough.0, %finish.0
+      %ex.0 = landingpad { ptr, i32 }
+              catch ptr null
+      br label %catch.0
+
+    catch.0:                                          ; preds = %pad.0
+      call void @checkdefer(ptr nest undef, ptr %y)
+      br label %finish.0
+
+    finish.0:                                         ; preds = %catch.0, %finally.0
+      invoke void @deferreturn(ptr nest undef, ptr %y)
+              to label %cont.0 unwind label %pad.0
+
+    cont.0:                                           ; preds = %fallthrough.0, %finish.0
+      %fload.0 = load i8, ptr %finvar.0, align 1
+      %icmp.0 = icmp eq i8 %fload.0, 1
+      br i1 %icmp.0, label %finret.0, label %finres.0
+
+    pad.1:                                            ; preds = %then.0, %cont.1, %entry
+      %ex.1 = landingpad { ptr, i32 }
+              catch ptr null
+      br label %catch.1
+
+    catch.1:                                          ; preds = %pad.1
+      invoke void @plix(ptr nest undef)
+              to label %cont.4 unwind label %catchpad.0
+
+    catchpad.0:                                       ; preds = %catch.1
+      %ex2.0 = landingpad { ptr, i32 }
+              cleanup
+      store { ptr, i32 } %ex2.0, ptr %ehtmp.0, align 8
+      store i8 0, ptr %finvar.0, align 1
+      br label %finally.0
+
+    cont.1:                                           ; preds = %entry
+      store i64 %call.0, ptr %x, align 8
+      invoke void @plark(ptr nest undef)
+              to label %cont.2 unwind label %pad.1
+
+    cont.2:                                           ; preds = %cont.1
+      br i1 false, label %then.0, label %else.0
+
+    then.0:                                           ; preds = %cont.2
+      %call.1 = invoke i16 @noret(ptr nest undef)
+              to label %cont.3 unwind label %pad.1
+
+    fallthrough.0:                                    ; preds = %else.0
+      store i64 123, ptr %x, align 8
+      store i8 1, ptr %finvar.0, align 1
+      invoke void @deferreturn(ptr nest undef, ptr %y)
+              to label %cont.0 unwind label %pad.0
+
+    else.0:                                           ; preds = %cont.2
+      br label %fallthrough.0
+
+    cont.3:                                           ; preds = %then.0
+      unreachable
+
+    cont.4:                                           ; preds = %catch.1
+      br label %finok.0
+
+    finres.0:                                         ; preds = %cont.0
+      %excv.0 = load { ptr, i32 }, ptr %ehtmp.0, align 8
+      resume { ptr, i32 } %excv.0
+
+    finret.0:                                         ; preds = %cont.0
+      ret void
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -886,90 +886,91 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define i64 @baz(i8* nest %nest.0, i64 %p0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 {
-  entry:
-    %ehtmp.0 = alloca { i8*, i32 }, align 8
-    %p0.addr = alloca i64, align 8
-    %ret = alloca i64, align 8
-    %x = alloca i8, align 1
-    %finvar.0 = alloca i8, align 1
-    store i64 %p0, i64* %p0.addr, align 8
-    store i64 0, i64* %ret, align 8
-    store i8 0, i8* %x, align 1
-    %call.0 = invoke i64 @splat(i8* nest undef, i64 99)
-            to label %cont.1 unwind label %pad.1
-  
-  finok.0:                                          ; preds = %cont.2
-    store i8 1, i8* %finvar.0, align 1
-    br label %finally.0
-  
-  finally.0:                                        ; preds = %catchpad.0, %finok.0
-    br label %finish.0
-  
-  pad.0:                                            ; preds = %else.0, %then.0, %finish.0
-    %ex.0 = landingpad { i8*, i32 }
-            catch i8* null
-    br label %catch.0
-  
-  catch.0:                                          ; preds = %pad.0
-    call void @checkdefer(i8* nest undef, i8* %x)
-    br label %finish.0
-  
-  finish.0:                                         ; preds = %catch.0, %finally.0
-    invoke void @deferreturn(i8* nest undef, i8* %x)
-            to label %cont.0 unwind label %pad.0
-  
-  cont.0:                                           ; preds = %else.0, %then.0, %finish.0
-    %fload.0 = load i8, i8* %finvar.0, align 1
-    %icmp.1 = icmp eq i8 %fload.0, 1
-    br i1 %icmp.1, label %finret.0, label %finres.0
-  
-  pad.1:                                            ; preds = %entry
-    %ex.1 = landingpad { i8*, i32 }
-            catch i8* null
-    br label %catch.1
-  
-  catch.1:                                          ; preds = %pad.1
-    %call.1 = invoke i64 @splat(i8* nest undef, i64 13)
-            to label %cont.2 unwind label %catchpad.0
-  
-  catchpad.0:                                       ; preds = %catch.1
-    %ex2.0 = landingpad { i8*, i32 }
-            cleanup
-    store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0, align 8
-    store i8 0, i8* %finvar.0, align 1
-    br label %finally.0
-  
-  cont.1:                                           ; preds = %entry
-    %icmp.0 = icmp eq i64 %call.0, 88
-    %zext.0 = zext i1 %icmp.0 to i8
-    %trunc.0 = trunc i8 %zext.0 to i1
-    br i1 %trunc.0, label %then.0, label %else.0
-  
-  then.0:                                           ; preds = %cont.1
-    store i64 22, i64* %ret, align 8
-    store i8 1, i8* %finvar.0, align 1
-    invoke void @deferreturn(i8* nest undef, i8* %x)
-            to label %cont.0 unwind label %pad.0
-  
-  else.0:                                           ; preds = %cont.1
-    %p0.ld.0 = load i64, i64* %p0.addr, align 8
-    store i64 %p0.ld.0, i64* %ret, align 8
-    store i8 1, i8* %finvar.0, align 1
-    invoke void @deferreturn(i8* nest undef, i8* %x)
-            to label %cont.0 unwind label %pad.0
-  
-  cont.2:                                           ; preds = %catch.1
-    br label %finok.0
-  
-  finres.0:                                         ; preds = %cont.0
-    %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0, align 8
-    resume { i8*, i32 } %excv.0
-  
-  finret.0:                                         ; preds = %cont.0
-    %ret.ld.1 = load i64, i64* %ret, align 8
-    ret i64 %ret.ld.1
-  }
+    define i64 @baz(ptr nest %nest.0, i64 %p0) #0 personality ptr @__gccgo_personality_v0 {
+    entry:
+      %ehtmp.0 = alloca { ptr, i32 }, align 8
+      %p0.addr = alloca i64, align 8
+      %ret = alloca i64, align 8
+      %x = alloca i8, align 1
+      %finvar.0 = alloca i8, align 1
+      store i64 %p0, ptr %p0.addr, align 8
+      store i64 0, ptr %ret, align 8
+      store i8 0, ptr %x, align 1
+      %call.0 = invoke i64 @splat(ptr nest undef, i64 99)
+              to label %cont.1 unwind label %pad.1
+
+    finok.0:                                          ; preds = %cont.2
+      store i8 1, ptr %finvar.0, align 1
+      br label %finally.0
+
+    finally.0:                                        ; preds = %catchpad.0, %finok.0
+      br label %finish.0
+
+    pad.0:                                            ; preds = %else.0, %then.0, %finish.0
+      %ex.0 = landingpad { ptr, i32 }
+              catch ptr null
+      br label %catch.0
+
+    catch.0:                                          ; preds = %pad.0
+      call void @checkdefer(ptr nest undef, ptr %x)
+      br label %finish.0
+
+    finish.0:                                         ; preds = %catch.0, %finally.0
+      invoke void @deferreturn(ptr nest undef, ptr %x)
+              to label %cont.0 unwind label %pad.0
+
+    cont.0:                                           ; preds = %else.0, %then.0, %finish.0
+      %fload.0 = load i8, ptr %finvar.0, align 1
+      %icmp.1 = icmp eq i8 %fload.0, 1
+      br i1 %icmp.1, label %finret.0, label %finres.0
+
+    pad.1:                                            ; preds = %entry
+      %ex.1 = landingpad { ptr, i32 }
+              catch ptr null
+      br label %catch.1
+
+    catch.1:                                          ; preds = %pad.1
+      %call.1 = invoke i64 @splat(ptr nest undef, i64 13)
+              to label %cont.2 unwind label %catchpad.0
+
+    catchpad.0:                                       ; preds = %catch.1
+      %ex2.0 = landingpad { ptr, i32 }
+              cleanup
+      store { ptr, i32 } %ex2.0, ptr %ehtmp.0, align 8
+      store i8 0, ptr %finvar.0, align 1
+      br label %finally.0
+
+    cont.1:                                           ; preds = %entry
+      %icmp.0 = icmp eq i64 %call.0, 88
+
+      %zext.0 = zext i1 %icmp.0 to i8
+      %trunc.0 = trunc i8 %zext.0 to i1
+      br i1 %trunc.0, label %then.0, label %else.0
+
+    then.0:                                           ; preds = %cont.1
+      store i64 22, ptr %ret, align 8
+      store i8 1, ptr %finvar.0, align 1
+      invoke void @deferreturn(ptr nest undef, ptr %x)
+              to label %cont.0 unwind label %pad.0
+
+    else.0:                                           ; preds = %cont.1
+      %p0.ld.0 = load i64, ptr %p0.addr, align 8
+      store i64 %p0.ld.0, ptr %ret, align 8
+      store i8 1, ptr %finvar.0, align 1
+      invoke void @deferreturn(ptr nest undef, ptr %x)
+              to label %cont.0 unwind label %pad.0
+
+    cont.2:                                           ; preds = %catch.1
+      br label %finok.0
+
+    finres.0:                                         ; preds = %cont.0
+      %excv.0 = load { ptr, i32 }, ptr %ehtmp.0, align 8
+      resume { ptr, i32 } %excv.0
+
+    finret.0:                                         ; preds = %cont.0
+      %ret.ld.1 = load i64, ptr %ret, align 8
+      ret i64 %ret.ld.1
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
diff --git a/unittests/BackendCore/BackendVarTests.cpp b/unittests/BackendCore/BackendVarTests.cpp
index 526cd9d..48da657 100644
--- a/unittests/BackendCore/BackendVarTests.cpp
+++ b/unittests/BackendCore/BackendVarTests.cpp
@@ -66,7 +66,7 @@
   ASSERT_TRUE(ve2 != nullptr);
   Bstatement *es = h.mkExprStmt(ve2);
   EXPECT_EQ(repr(ve2->value()), "%loc1 = alloca i64, align 8");
-  EXPECT_EQ(repr(es), "%loc1.ld.0 = load i64, i64* %loc1, align 8");
+  EXPECT_EQ(repr(es), "%loc1.ld.0 = load i64, ptr %loc1, align 8");
 
   // Make sure error detection is working
   Bvariable *loce = be->local_variable(func1, "", be->error_type(), nullptr,
@@ -159,7 +159,7 @@
                                            false, loc, &inits);
   ASSERT_TRUE(tvar != nullptr);
   ASSERT_TRUE(inits != nullptr);
-  EXPECT_EQ(repr(inits), "store i64 64, i64* %tmpv.0, align 8");
+  EXPECT_EQ(repr(inits), "store i64 64, ptr %tmpv.0, align 8");
 
   h.addStmt(inits);
 
@@ -302,8 +302,7 @@
 
   {
     DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-      @desc = internal constant { i64 } { i64 ptrtoint
-      (i64 (i8*, i32, i32, i64*)* @foo to i64) }
+      @desc = internal constant { i64 } { i64 ptrtoint (ptr @foo to i64) }
     )RAW_RESULT");
 
     bool isOK = h.expectValue(ims->value(), exp);
@@ -460,8 +459,7 @@
   ASSERT_TRUE(gv != nullptr);
   EXPECT_TRUE(isa<GlobalVariable>(gv->value()));
   EXPECT_EQ(repr(gv->value()), "@x = global { i32 } zeroinitializer");
-  EXPECT_EQ(repr(gvdecl->value()),
-            "i32* getelementptr inbounds ({ i32 }, { i32 }* @x, i32 0, i32 0)");
+  EXPECT_EQ(repr(gvdecl->value()), "@x = global { i32 } zeroinitializer");
 
   // Create them in the other order: definition first,
   // then external declaration.
@@ -478,8 +476,7 @@
   ASSERT_TRUE(gvdecl2 != nullptr);
   EXPECT_TRUE(isa<GlobalVariable>(gv2->value()));
   EXPECT_EQ(repr(gv2->value()), "@y = global { i32 } zeroinitializer");
-  EXPECT_EQ(repr(gvdecl2->value()),
-      "i32* getelementptr inbounds ({ i32 }, { i32 }* @y, i32 0, i32 0)");
+  EXPECT_EQ(repr(gvdecl2->value()), "@y = global { i32 } zeroinitializer");
 
   bool broken = h.finish(PreserveDebugInfo);
   EXPECT_FALSE(broken && "Module failed to verify.");
@@ -519,26 +516,21 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
-  entry:
-    %x = alloca i32, align 4
-    %y = alloca { i32, i32 }, align 4
-    %0 = bitcast i32* %x to i8*
-    call void @llvm.lifetime.start.p0i8(i64 4, i8* %0)
-    %1 = bitcast { i32, i32 }* %y to i8*
-    call void @llvm.lifetime.start.p0i8(i64 8, i8* %1)
-    store i32 0, i32* %x, align 4
-    %cast.0 = bitcast { i32, i32 }* %y to i8*
-    call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 bitcast ({ i32, i32 }* @const.0 to i8*), i64 8, i1 false)
-    %field.0 = getelementptr inbounds { i32, i32 }, { i32, i32 }* %y, i32 0, i32 1
-    %y.field.ld.0 = load i32, i32* %field.0, align 4
-    store i32 %y.field.ld.0, i32* %x, align 4
-    %2 = bitcast i32* %x to i8*
-    call void @llvm.lifetime.end.p0i8(i64 4, i8* %2)
-    %3 = bitcast { i32, i32 }* %y to i8*
-    call void @llvm.lifetime.end.p0i8(i64 8, i8* %3)
-    ret void
-  }
+    define void @foo(ptr nest %nest.0) #0 {
+    entry:
+      %x = alloca i32, align 4
+      %y = alloca { i32, i32 }, align 4
+      call void @llvm.lifetime.start.p0(i64 4, ptr %x)
+      call void @llvm.lifetime.start.p0(i64 8, ptr %y)
+      store i32 0, ptr %x, align 4
+      call void @llvm.memcpy.p0.p0.i64(ptr align 4 %y, ptr align 4 @const.0, i64 8, i1 false)
+      %field.0 = getelementptr inbounds { i32, i32 }, ptr %y, i32 0, i32 1
+      %y.field.ld.0 = load i32, ptr %field.0, align 4
+      store i32 %y.field.ld.0, ptr %x, align 4
+      call void @llvm.lifetime.end.p0(i64 4, ptr %x)
+      call void @llvm.lifetime.end.p0(i64 8, ptr %y)
+      ret void
+    }
   )RAW_RESULT");
 
   bool isOK = h.expectValue(func->function(), exp);
@@ -659,12 +651,12 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 
   DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
-    define void @foo(i8* nest %nest.0) #0 {
-  entry:
-    %localemptys2f = alloca { {}, {} }, align 1
-    %localemptyintar = alloca [0 x i32], align 4
-    ret void
-  }
+    define void @foo(ptr nest %nest.0) #0 {
+    entry:
+      %localemptys2f = alloca { {}, {} }, align 1
+      %localemptyintar = alloca [0 x i32], align 4
+      ret void
+    }
   )RAW_RESULT");
   bool isOK = h.expectValue(func->function(), exp);
   EXPECT_TRUE(isOK && "Value does not have expected contents");
@@ -711,7 +703,7 @@
 
   // Expect to see only one lifetime start, since A) loc1 is at the top
   // level, and B) loc2 uses loc1 as declVar.
-  const char *expected = "call void @llvm.lifetime.start.p0i8(i64";
+  const char *expected = "call void @llvm.lifetime.start.p0(i64";
   EXPECT_EQ(h.countInstancesInModuleDump(expected), 1u);
 }
 
diff --git a/unittests/BackendCore/TestUtils.h b/unittests/BackendCore/TestUtils.h
index fa6774e..14dc355 100644
--- a/unittests/BackendCore/TestUtils.h
+++ b/unittests/BackendCore/TestUtils.h
@@ -10,8 +10,10 @@
 #define GOLLVM_UNITTESTS_BACKENDCORE_TESTUTILS_H
 
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/Type.h"
 #include "llvm/IR/Value.h"
 #include "llvm/IR/Verifier.h"
diff --git a/unittests/Driver/DriverTests.cpp b/unittests/Driver/DriverTests.cpp
index 80ddcc2..93bd287 100644
--- a/unittests/Driver/DriverTests.cpp
+++ b/unittests/Driver/DriverTests.cpp
@@ -56,7 +56,7 @@
   std::unique_ptr<opt::OptTable> opts =
       gollvm::options::createGollvmDriverOptTable();
   unsigned missingArgIndex, missingArgCount;
-  ArrayRef<const char *> argvv = makeArrayRef(args_);
+  ArrayRef<const char *> argvv(args_);
   opt::InputArgList args =
       opts->ParseArgs(argvv, missingArgIndex, missingArgCount);
 
diff --git a/unittests/TestUtils/DiffUtils.cpp b/unittests/TestUtils/DiffUtils.cpp
index 98786d7..e4d8a7e 100644
--- a/unittests/TestUtils/DiffUtils.cpp
+++ b/unittests/TestUtils/DiffUtils.cpp
@@ -30,9 +30,9 @@
 // ending line (20). Which one will matter for the machinery that does
 // test remastering.
 static bool macroLineAtStart() {
-  if (baseline.line == 16) {
+  if (baseline.line == 17) {
     return true;
-  } else if (baseline.line == 20) {
+  } else if (baseline.line == 21) {
     return false;
   } else {
     assert(false && "macroLineAtStart broken -- source edited?");