Skip to content

Commit

Permalink
[WebAssembly] Add RefTypeMem2Local pass
Browse files Browse the repository at this point in the history
This adds `WebAssemblyRefTypeMem2Local` pass, which changes the address
spaces of reference type `alloca`s to `addrspace(1)`. This in turn
changes the address spaces of all `load` and `store` instructions that
use the `alloca`s.

`addrspace(1)` is `WASM_ADDRESS_SPACE_VAR`, and loads and stores to this
address space become `local.get`s and `local.set`s, thanks to the Wasm
local IR support added in
llvm@82f92e3.

In a follow-up PR, I am planning to replace the usage of mem2reg pass
with this to solve the reference type `alloca` problems described in
 llvm#81575.
  • Loading branch information
aheejin committed Feb 16, 2024
1 parent bfe302c commit 58beb2a
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 0 deletions.
1 change: 1 addition & 0 deletions llvm/lib/Target/WebAssembly/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyOptimizeLiveIntervals.cpp
WebAssemblyOptimizeReturned.cpp
WebAssemblyPeephole.cpp
WebAssemblyRefTypeMem2Local.cpp
WebAssemblyRegisterInfo.cpp
WebAssemblyRegColoring.cpp
WebAssemblyRegNumbering.cpp
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ModulePass *createWebAssemblyAddMissingPrototypes();
ModulePass *createWebAssemblyFixFunctionBitcasts();
FunctionPass *createWebAssemblyOptimizeReturned();
FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv();
FunctionPass *createWebAssemblyRefTypeMem2Local();

// ISel and immediate followup passes.
FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
Expand Down Expand Up @@ -59,6 +60,7 @@ ModulePass *createWebAssemblyMCLowerPrePass();
// PassRegistry initialization declarations.
void initializeFixFunctionBitcastsPass(PassRegistry &);
void initializeOptimizeReturnedPass(PassRegistry &);
void initializeWebAssemblyRefTypeMem2LocalPass(PassRegistry &);
void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &);
void initializeWebAssemblyArgumentMovePass(PassRegistry &);
void initializeWebAssemblyCFGSortPass(PassRegistry &);
Expand Down
91 changes: 91 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//=== WebAssemblyRefTypeMem2Local.cpp - WebAssembly RefType Mem2Local -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Assign reference type allocas to local addrspace (addrspace(1)) so that
/// their loads and stores can be lowered to local.gets/local.sets.
///
//===----------------------------------------------------------------------===//

#include "Utils/WasmAddressSpaces.h"
#include "Utils/WebAssemblyTypeUtilities.h"
#include "WebAssembly.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Pass.h"
using namespace llvm;

#define DEBUG_TYPE "wasm-ref-type-mem2local"

namespace {
class WebAssemblyRefTypeMem2Local final
: public FunctionPass,
public InstVisitor<WebAssemblyRefTypeMem2Local> {
StringRef getPassName() const override {
return "WebAssembly Refernce Types Memory to Local";
}

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
FunctionPass::getAnalysisUsage(AU);
}

bool runOnFunction(Function &F) override;
bool Changed = false;

public:
static char ID;
WebAssemblyRefTypeMem2Local() : FunctionPass(ID) {}

void visitAllocaInst(AllocaInst &AI);
};
} // End anonymous namespace

char WebAssemblyRefTypeMem2Local::ID = 0;
INITIALIZE_PASS(WebAssemblyRefTypeMem2Local, DEBUG_TYPE,
"Assign reference type allocas to local address space", true,
false)

FunctionPass *llvm::createWebAssemblyRefTypeMem2Local() {
return new WebAssemblyRefTypeMem2Local();
}

void WebAssemblyRefTypeMem2Local::visitAllocaInst(AllocaInst &AI) {
if (WebAssembly::isWebAssemblyReferenceType(AI.getAllocatedType())) {
Changed = true;
IRBuilder<> IRB(AI.getContext());
IRB.SetInsertPoint(&AI);
auto *NewAI = IRB.CreateAlloca(AI.getAllocatedType(),
WebAssembly::WASM_ADDRESS_SPACE_VAR, nullptr,
AI.getName() + ".var");

// The below is basically equivalent to AI.replaceAllUsesWith(NewAI), but we
// cannot use it because it requires the old and new types be the same,
// which is not true here because the address spaces are different.
if (AI.hasValueHandle())
ValueHandleBase::ValueIsRAUWd(&AI, NewAI);
if (AI.isUsedByMetadata())
ValueAsMetadata::handleRAUW(&AI, NewAI);
while (!AI.materialized_use_empty()) {
Use &U = *AI.materialized_use_begin();
U.set(NewAI);
}

AI.eraseFromParent();
}
}

bool WebAssemblyRefTypeMem2Local::runOnFunction(Function &F) {
LLVM_DEBUG(dbgs() << "********** WebAssembly RefType Mem2Local **********\n"
"********** Function: "
<< F.getName() << '\n');

visit(F);
return Changed;
}
1 change: 1 addition & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
initializeLowerGlobalDtorsLegacyPassPass(PR);
initializeFixFunctionBitcastsPass(PR);
initializeOptimizeReturnedPass(PR);
initializeWebAssemblyRefTypeMem2LocalPass(PR);
initializeWebAssemblyArgumentMovePass(PR);
initializeWebAssemblySetP2AlignOperandsPass(PR);
initializeWebAssemblyReplacePhysRegsPass(PR);
Expand Down
39 changes: 39 additions & 0 deletions llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
; RUN: opt < %s -wasm-ref-type-mem2local -S | FileCheck %s

target triple = "wasm32-unknown-unknown"

%externref = type ptr addrspace(10)
%funcref = type ptr addrspace(20)

declare %funcref @get_funcref()
declare %externref @get_externref()
declare void @take_funcref(%funcref)
declare void @take_externref(%externref)

; CHECK-LABEL: @test_ref_type_mem2local
define void @test_ref_type_mem2local() {
entry:
%alloc.externref = alloca %externref, align 1
%eref = call %externref @get_externref()
store %externref %eref, ptr %alloc.externref, align 1
%eref.loaded = load %externref, ptr %alloc.externref, align 1
call void @take_externref(%externref %eref.loaded)
; CHECK: %alloc.externref.var = alloca ptr addrspace(10), align 1, addrspace(1)
; CHECK-NEXT: %eref = call ptr addrspace(10) @get_externref()
; CHECK-NEXT: store ptr addrspace(10) %eref, ptr addrspace(1) %alloc.externref.var, align 1
; CHECK-NEXT: %eref.loaded = load ptr addrspace(10), ptr addrspace(1) %alloc.externref.var, align 1
; CHECK-NEXT: call void @take_externref(ptr addrspace(10) %eref.loaded)

%alloc.funcref = alloca %funcref, align 1
%fref = call %funcref @get_funcref()
store %funcref %fref, ptr %alloc.funcref, align 1
%fref.loaded = load %funcref, ptr %alloc.funcref, align 1
call void @take_funcref(%funcref %fref.loaded)
; CHECK-NEXT: %alloc.funcref.var = alloca ptr addrspace(20), align 1, addrspace(1)
; CHECK-NEXT: %fref = call ptr addrspace(20) @get_funcref()
; CHECK-NEXT: store ptr addrspace(20) %fref, ptr addrspace(1) %alloc.funcref.var, align 1
; CHECK-NEXT: %fref.loaded = load ptr addrspace(20), ptr addrspace(1) %alloc.funcref.var, align 1
; CHECK-NEXT: call void @take_funcref(ptr addrspace(20) %fref.loaded)

ret void
}

0 comments on commit 58beb2a

Please sign in to comment.