Skip to content

Commit

Permalink
[WebAssembly] Add RefTypeMem2Local pass (llvm#81965)
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 authored Feb 27, 2024
1 parent 70a7b1e commit d4cdb51
Show file tree
Hide file tree
Showing 5 changed files with 152 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 Reference 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 @@ -77,6 +77,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
initializeLowerGlobalDtorsLegacyPassPass(PR);
initializeFixFunctionBitcastsPass(PR);
initializeOptimizeReturnedPass(PR);
initializeWebAssemblyRefTypeMem2LocalPass(PR);
initializeWebAssemblyArgumentMovePass(PR);
initializeWebAssemblySetP2AlignOperandsPass(PR);
initializeWebAssemblyReplacePhysRegsPass(PR);
Expand Down
57 changes: 57 additions & 0 deletions llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
; 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 %externref @get_externref()
declare %funcref @get_funcref()
declare i32 @get_i32()
declare void @take_externref(%externref)
declare void @take_funcref(%funcref)
declare void @take_i32(i32)

; Reference type allocas should be moved to addrspace(1)
; 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
}

; POD type allocas should stay the same
; CHECK-LABEL: @test_pod_type
define void @test_pod_type() {
entry:
%alloc.i32 = alloca i32
%i32 = call i32 @get_i32()
store i32 %i32, ptr %alloc.i32
%i32.loaded = load i32, ptr %alloc.i32
call void @take_i32(i32 %i32.loaded)
; CHECK: %alloc.i32 = alloca i32, align 4{{$}}
; CHECK-NOT: addrspace(1)

ret void
}

0 comments on commit d4cdb51

Please sign in to comment.