Repository: tetratelabs/wazero
Pull request #1141 (2023-02-20 06:47:22)

wazeroir: less allocations with Labels
The following is the bench result: 
```
name         old time/op    new time/op    delta
_compile-10     1.37s ± 3%     1.35s ± 3%  -1.02%  (p=0.001 n=17+18)

name         old alloc/op   new alloc/op   delta
_compile-10     652MB ± 0%     646MB ± 0%  -0.98%  (p=0.000 n=19+19)

name         old allocs/op  new allocs/op  delta
_compile-10     11.5M ± 0%     10.7M ± 0%  -6.88%  (p=0.000 n=19+20)
```


Patch:
diff --git a/internal/engine/compiler/impl_amd64.go b/internal/engine/compiler/impl_amd64.go
index 0040505f..bed412dd 100644
--- a/internal/engine/compiler/impl_amd64.go
+++ b/internal/engine/compiler/impl_amd64.go
@@ -386,7 +386,7 @@ func (c *amd64Compiler) compileBr(o wazeroir.OperationBr) error {
 }
 
 // branchInto adds instruction necessary to jump into the given branch target.
-func (c *amd64Compiler) branchInto(target *wazeroir.BranchTarget) error {
+func (c *amd64Compiler) branchInto(target wazeroir.Label) error {
 	if target.IsReturnTarget() {
 		return c.compileReturnFunction()
 	} else {
@@ -487,7 +487,7 @@ func (c *amd64Compiler) compileBrIf(o wazeroir.OperationBrIf) error {
 			return err
 		}
 	} else {
-		elseLabelKey := elseTarget.Target.Label.String()
+		elseLabelKey := elseTarget.Target.String()
 		if c.ir.LabelCallers[elseLabelKey] > 1 {
 			// We can only re-use register state if when there's a single call-site.
 			// Release existing values on registers to the stack if there's multiple ones to have
@@ -517,7 +517,7 @@ func (c *amd64Compiler) compileBrIf(o wazeroir.OperationBrIf) error {
 	if thenTarget.Target.IsReturnTarget() {
 		return c.compileReturnFunction()
 	} else {
-		thenLabelKey := thenTarget.Target.Label.String()
+		thenLabelKey := thenTarget.Target.String()
 		if c.ir.LabelCallers[thenLabelKey] > 1 {
 			// We can only re-use register state if when there's a single call-site.
 			// Release existing values on registers to the stack if there's multiple ones to have
diff --git a/internal/engine/compiler/impl_arm64.go b/internal/engine/compiler/impl_arm64.go
index eb76dd00..0e3edd65 100644
--- a/internal/engine/compiler/impl_arm64.go
+++ b/internal/engine/compiler/impl_arm64.go
@@ -731,7 +731,7 @@ func (c *arm64Compiler) compileBrIf(o wazeroir.OperationBrIf) error {
 	return c.compileBranchInto(o.Then.Target)
 }
 
-func (c *arm64Compiler) compileBranchInto(target *wazeroir.BranchTarget) error {
+func (c *arm64Compiler) compileBranchInto(target wazeroir.Label) error {
 	if target.IsReturnTarget() {
 		return c.compileReturnFunction()
 	} else {
diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go
index 7e19b114..c8b3f849 100644
--- a/internal/engine/interpreter/interpreter.go
+++ b/internal/engine/interpreter/interpreter.go
@@ -336,7 +336,7 @@ func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) {
 		case wazeroir.OperationBrIf:
 			op.rs = make([]*wazeroir.InclusiveRange, 2)
 			op.us = make([]uint64, 2)
-			for i, target := range []*wazeroir.BranchTargetDrop{o.Then, o.Else} {
+			for i, target := range []wazeroir.BranchTargetDrop{o.Then, o.Else} {
 				op.rs[i] = target.ToDrop
 				if target.Target.IsReturnTarget() {
 					// Jmp to the end of the possible binary.
diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go
index 4f97f9ca..756f2f2d 100644
--- a/internal/wazeroir/compiler.go
+++ b/internal/wazeroir/compiler.go
@@ -45,19 +45,18 @@ func (c *controlFrame) ensureContinuation() {
 	}
 }
 
-func (c *controlFrame) asBranchTarget() *BranchTarget {
+func (c *controlFrame) asLabel() Label {
 	switch c.kind {
 	case controlFrameKindBlockWithContinuationLabel,
 		controlFrameKindBlockWithoutContinuationLabel:
-		return &BranchTarget{Label: &Label{FrameID: c.frameID, Kind: LabelKindContinuation}}
+		return Label{FrameID: c.frameID, Kind: LabelKindContinuation}
 	case controlFrameKindLoop:
-		return &BranchTarget{Label: &Label{FrameID: c.frameID, Kind: LabelKindHeader}}
+		return Label{FrameID: c.frameID, Kind: LabelKindHeader}
 	case controlFrameKindFunction:
-		// Note nil target is translated as return.
-		return &BranchTarget{Label: nil}
+		return Label{Kind: LabelKindReturn}
 	case controlFrameKindIfWithElse,
 		controlFrameKindIfWithoutElse:
-		return &BranchTarget{Label: &Label{FrameID: c.frameID, Kind: LabelKindContinuation}}
+		return Label{FrameID: c.frameID, Kind: LabelKindContinuation}
 	}
 	panic(fmt.Sprintf("unreachable: a bug in wazeroir implementation: %v", c.kind))
 }
@@ -469,13 +468,13 @@ operatorSwitch:
 		c.controlFrames.push(frame)
 
 		// Prep labels for inside and the continuation of this loop.
-		loopLabel := &Label{FrameID: frame.frameID, Kind: LabelKindHeader}
+		loopLabel := Label{FrameID: frame.frameID, Kind: LabelKindHeader}
 		c.result.LabelCallers[loopLabel.String()]++
 
 		// Emit the branch operation to enter inside the loop.
 		c.emit(
 			OperationBr{
-				Target: loopLabel.asBranchTarget(),
+				Target: loopLabel,
 			},
 			OperationLabel{Label: loopLabel},
 		)
@@ -516,8 +515,8 @@ operatorSwitch:
 		c.controlFrames.push(frame)
 
 		// Prep labels for if and else of this if.
-		thenLabel := &Label{Kind: LabelKindHeader, FrameID: frame.frameID}
-		elseLabel := &Label{Kind: LabelKindElse, FrameID: frame.frameID}
+		thenLabel := Label{Kind: LabelKindHeader, FrameID: frame.frameID}
+		elseLabel := Label{Kind: LabelKindElse, FrameID: frame.frameID}
 		c.result.LabelCallers[thenLabel.String()]++
 		c.result.LabelCallers[elseLabel.String()]++
 
@@ -551,7 +550,7 @@ operatorSwitch:
 
 			// We are no longer unreachable in else frame,
 			// so emit the correct label, and reset the unreachable state.
-			elseLabel := &Label{FrameID: frame.frameID, Kind: LabelKindElse}
+			elseLabel := Label{FrameID: frame.frameID, Kind: LabelKindElse}
 			c.resetUnreachable()
 			c.emit(
 				OperationLabel{Label: elseLabel},
@@ -576,8 +575,8 @@ operatorSwitch:
 		}
 
 		// Prep labels for else and the continuation of this if block.
-		elseLabel := &Label{FrameID: frame.frameID, Kind: LabelKindElse}
-		continuationLabel := &Label{FrameID: frame.frameID, Kind: LabelKindContinuation}
+		elseLabel := Label{FrameID: frame.frameID, Kind: LabelKindElse}
+		continuationLabel := Label{FrameID: frame.frameID, Kind: LabelKindContinuation}
 		c.result.LabelCallers[continuationLabel.String()]++
 
 		// Emit the instructions for exiting the if loop,
@@ -585,7 +584,7 @@ operatorSwitch:
 		c.emit(
 			dropOp,
 			// Jump to the continuation of this block.
-			OperationBr{Target: continuationLabel.asBranchTarget()},
+			OperationBr{Target: continuationLabel},
 			// Initiate the else block.
 			OperationLabel{Label: elseLabel},
 		)
@@ -606,14 +605,14 @@ operatorSwitch:
 				c.stackPush(wasmValueTypeToUnsignedType(t))
 			}
 
-			continuationLabel := &Label{FrameID: frame.frameID, Kind: LabelKindContinuation}
+			continuationLabel := Label{FrameID: frame.frameID, Kind: LabelKindContinuation}
 			if frame.kind == controlFrameKindIfWithoutElse {
 				// Emit the else label.
-				elseLabel := &Label{Kind: LabelKindElse, FrameID: frame.frameID}
+				elseLabel := Label{Kind: LabelKindElse, FrameID: frame.frameID}
 				c.result.LabelCallers[continuationLabel.String()]++
 				c.emit(
 					OperationLabel{Label: elseLabel},
-					OperationBr{Target: continuationLabel.asBranchTarget()},
+					OperationBr{Target: continuationLabel},
 					OperationLabel{Label: continuationLabel},
 				)
 			} else {
@@ -647,30 +646,29 @@ operatorSwitch:
 			// Return from function.
 			c.emit(
 				dropOp,
-				// Pass empty target instead of nil to avoid misinterpretation as "return"
-				OperationBr{Target: &BranchTarget{}},
+				OperationBr{Target: Label{Kind: LabelKindReturn}},
 			)
 		case controlFrameKindIfWithoutElse:
 			// This case we have to emit "empty" else label.
-			elseLabel := &Label{Kind: LabelKindElse, FrameID: frame.frameID}
-			continuationLabel := &Label{Kind: LabelKindContinuation, FrameID: frame.frameID}
+			elseLabel := Label{Kind: LabelKindElse, FrameID: frame.frameID}
+			continuationLabel := Label{Kind: LabelKindContinuation, FrameID: frame.frameID}
 			c.result.LabelCallers[continuationLabel.String()] += 2
 			c.emit(
 				dropOp,
-				OperationBr{Target: continuationLabel.asBranchTarget()},
+				OperationBr{Target: continuationLabel},
 				// Emit the else which soon branches into the continuation.
 				OperationLabel{Label: elseLabel},
-				OperationBr{Target: continuationLabel.asBranchTarget()},
+				OperationBr{Target: continuationLabel},
 				// Initiate the continuation.
 				OperationLabel{Label: continuationLabel},
 			)
 		case controlFrameKindBlockWithContinuationLabel,
 			controlFrameKindIfWithElse:
-			continuationLabel := &Label{Kind: LabelKindContinuation, FrameID: frame.frameID}
+			continuationLabel := Label{Kind: LabelKindContinuation, FrameID: frame.frameID}
 			c.result.LabelCallers[continuationLabel.String()]++
 			c.emit(
 				dropOp,
-				OperationBr{Target: continuationLabel.asBranchTarget()},
+				OperationBr{Target: continuationLabel},
 				OperationLabel{Label: continuationLabel},
 			)
 		case controlFrameKindLoop, controlFrameKindBlockWithoutContinuationLabel:
@@ -697,8 +695,8 @@ operatorSwitch:
 		targetFrame := c.controlFrames.get(int(targetIndex))
 		targetFrame.ensureContinuation()
 		dropOp := OperationDrop{Depth: c.getFrameDropRange(targetFrame, false)}
-		target := targetFrame.asBranchTarget()
-		c.result.LabelCallers[target.Label.String()]++
+		target := targetFrame.asLabel()
+		c.result.LabelCallers[target.String()]++
 		c.emit(
 			dropOp,
 			OperationBr{Target: target},
@@ -722,14 +720,14 @@ operatorSwitch:
 		targetFrame := c.controlFrames.get(int(targetIndex))
 		targetFrame.ensureContinuation()
 		drop := c.getFrameDropRange(targetFrame, false)
-		target := targetFrame.asBranchTarget()
-		c.result.LabelCallers[target.Label.String()]++
+		target := targetFrame.asLabel()
+		c.result.LabelCallers[target.String()]++
 
-		continuationLabel := &Label{FrameID: c.nextID(), Kind: LabelKindHeader}
+		continuationLabel := Label{FrameID: c.nextID(), Kind: LabelKindHeader}
 		c.result.LabelCallers[continuationLabel.String()]++
 		c.emit(
 			OperationBrIf{
-				Then: &BranchTargetDrop{ToDrop: drop, Target: target},
+				Then: BranchTargetDrop{ToDrop: drop, Target: target},
 				Else: continuationLabel.asBranchTargetDrop(),
 			},
 			// Start emitting else block operations.
@@ -770,9 +768,9 @@ operatorSwitch:
 			targetFrame := c.controlFrames.get(int(l))
 			targetFrame.ensureContinuation()
 			drop := c.getFrameDropRange(targetFrame, false)
-			target := &BranchTargetDrop{ToDrop: drop, Target: targetFrame.asBranchTarget()}
+			target := &BranchTargetDrop{ToDrop: drop, Target: targetFrame.asLabel()}
 			targets[i] = target
-			c.result.LabelCallers[target.Target.Label.String()]++
+			c.result.LabelCallers[target.Target.String()]++
 		}
 
 		// Prep default target control frame.
@@ -784,8 +782,8 @@ operatorSwitch:
 		defaultTargetFrame := c.controlFrames.get(int(l))
 		defaultTargetFrame.ensureContinuation()
 		defaultTargetDrop := c.getFrameDropRange(defaultTargetFrame, false)
-		defaultTarget := defaultTargetFrame.asBranchTarget()
-		c.result.LabelCallers[defaultTarget.Label.String()]++
+		defaultTarget := defaultTargetFrame.asLabel()
+		c.result.LabelCallers[defaultTarget.String()]++
 
 		c.emit(
 			OperationBrTable{
@@ -806,7 +804,7 @@ operatorSwitch:
 		// Cleanup the stack and then jmp to function frame's continuation (meaning return).
 		c.emit(
 			dropOp,
-			OperationBr{Target: functionFrame.asBranchTarget()},
+			OperationBr{Target: functionFrame.asLabel()},
 		)
 
 		// Return operation is stack-polymorphic, and mark the state as unreachable.
diff --git a/internal/wazeroir/format.go b/internal/wazeroir/format.go
index cb04e7fb..ea8e3fd1 100644
--- a/internal/wazeroir/format.go
+++ b/internal/wazeroir/format.go
@@ -28,7 +28,7 @@ func formatOperation(w io.StringWriter, b Operation) {
 		str = "unreachable"
 	case OperationLabel:
 		isLabel = true
-		str = fmt.Sprintf("%s:", o.Label.asBranchTarget())
+		str = fmt.Sprintf("%s:", o.Label)
 	case OperationBr:
 		str = fmt.Sprintf("br %s", o.Target.String())
 	case OperationBrIf:
diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go
index 01c7762b..1bf0e268 100644
--- a/internal/wazeroir/operations.go
+++ b/internal/wazeroir/operations.go
@@ -731,12 +731,7 @@ type Label struct {
 }
 
 // String implements fmt.Stringer.
-func (l *Label) String() (ret string) {
-	if l == nil {
-		// Sometimes String() is called on the nil label which is interpreted
-		// as the function return.
-		return ""
-	}
+func (l Label) String() (ret string) {
 	switch l.Kind {
 	case LabelKindHeader:
 		ret = fmt.Sprintf(".L%d", l.FrameID)
@@ -744,10 +739,16 @@ func (l *Label) String() (ret string) {
 		ret = fmt.Sprintf(".L%d_else", l.FrameID)
 	case LabelKindContinuation:
 		ret = fmt.Sprintf(".L%d_cont", l.FrameID)
+	case LabelKindReturn:
+		return ".return"
 	}
 	return
 }
 
+func (l Label) IsReturnTarget() bool {
+	return l.Kind == LabelKindReturn
+}
+
 // LabelKind is the kind of the label.
 type LabelKind = byte
 
@@ -766,47 +767,22 @@ const (
 	// )
 	// we have the continuation block (of if-block) corresponding to "return" opcode.
 	LabelKindContinuation
+	LabelKindReturn
 )
 
-func (l *Label) asBranchTarget() *BranchTarget {
-	return &BranchTarget{Label: l}
-}
-
-func (l *Label) asBranchTargetDrop() *BranchTargetDrop {
-	return &BranchTargetDrop{Target: l.asBranchTarget()}
-}
-
-// BranchTarget represents the branch operation's target such as OperationBr of OperationBrIf.
-type BranchTarget struct {
-	// Label holds the target label. Note that this is nullable and in that case
-	// the branch target is the "return" of the function.
-	Label *Label
-}
-
-// IsReturnTarget returns true if the branch target is the function return, false otherwise.
-func (b *BranchTarget) IsReturnTarget() bool {
-	return b.Label == nil
-}
-
-// String implements fmt.Stringer.
-func (b *BranchTarget) String() (ret string) {
-	if b.IsReturnTarget() {
-		ret = ".return"
-	} else {
-		ret = b.Label.String()
-	}
-	return
+func (l Label) asBranchTargetDrop() BranchTargetDrop {
+	return BranchTargetDrop{Target: l}
 }
 
 // BranchTargetDrop represents the branch target and the drop range which must be dropped
 // before give the control over to the target label.
 type BranchTargetDrop struct {
-	Target *BranchTarget
+	Target Label
 	ToDrop *InclusiveRange
 }
 
 // String implements fmt.Stringer.
-func (b *BranchTargetDrop) String() (ret string) {
+func (b BranchTargetDrop) String() (ret string) {
 	if b.ToDrop != nil {
 		ret = fmt.Sprintf("%s(drop %d..%d)", b.Target, b.ToDrop.Start, b.ToDrop.End)
 	} else {
@@ -831,7 +807,7 @@ func (OperationUnreachable) Kind() OperationKind {
 //
 // This is used to inform the engines of the beginning of a label.
 type OperationLabel struct {
-	Label *Label
+	Label Label
 }
 
 // Kind implements Operation.Kind
@@ -843,7 +819,7 @@ func (OperationLabel) Kind() OperationKind {
 //
 // The engines are expected to branch into OperationBr.Target label.
 type OperationBr struct {
-	Target *BranchTarget
+	Target Label
 }
 
 // Kind implements Operation.Kind
@@ -856,7 +832,7 @@ func (OperationBr) Kind() OperationKind {
 // The engines are expected to pop a value and branch into OperationBrIf.Then label if the value equals 1.
 // Otherwise, the code branches into OperationBrIf.Else label.
 type OperationBrIf struct {
-	Then, Else *BranchTargetDrop
+	Then, Else BranchTargetDrop
 }
 
 // Kind implements Operation.Kind


Test patch:
diff --git a/internal/engine/compiler/compiler_controlflow_test.go b/internal/engine/compiler/compiler_controlflow_test.go
index 127b6d98..ebd16072 100644
--- a/internal/engine/compiler/compiler_controlflow_test.go
+++ b/internal/engine/compiler/compiler_controlflow_test.go
@@ -37,7 +37,7 @@ func TestCompiler_compileHostFunction(t *testing.T) {
 }
 
 func TestCompiler_compileLabel(t *testing.T) {
-	label := &wazeroir.Label{FrameID: 100, Kind: wazeroir.LabelKindContinuation}
+	label := wazeroir.Label{FrameID: 100, Kind: wazeroir.LabelKindContinuation}
 	for _, expectSkip := range []bool{false, true} {
 		expectSkip := expectSkip
 		t.Run(fmt.Sprintf("expect skip=%v", expectSkip), func(t *testing.T) {
@@ -49,7 +49,7 @@ func TestCompiler_compileLabel(t *testing.T) {
 				actual := compiler.compileLabel(wazeroir.OperationLabel{Label: label})
 				require.True(t, actual)
 			} else {
-				err := compiler.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: label}})
+				err := compiler.compileBr(wazeroir.OperationBr{Target: label})
 				require.NoError(t, err)
 				actual := compiler.compileLabel(wazeroir.OperationLabel{Label: label})
 				require.False(t, actual)
@@ -60,8 +60,8 @@ func TestCompiler_compileLabel(t *testing.T) {
 
 func TestCompiler_compileBrIf(t *testing.T) {
 	unreachableStatus, thenLabelExitStatus, elseLabelExitStatus := nativeCallStatusCodeUnreachable, nativeCallStatusCodeUnreachable+1, nativeCallStatusCodeUnreachable+2
-	thenBranchTarget := &wazeroir.BranchTargetDrop{Target: &wazeroir.BranchTarget{Label: &wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 1}}}
-	elseBranchTarget := &wazeroir.BranchTargetDrop{Target: &wazeroir.BranchTarget{Label: &wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 2}}}
+	thenBranchTarget := wazeroir.BranchTargetDrop{Target: wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 1}}
+	elseBranchTarget := wazeroir.BranchTargetDrop{Target: wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 2}}
 
 	tests := []struct {
 		name      string
@@ -240,12 +240,12 @@ func TestCompiler_compileBrIf(t *testing.T) {
 					compiler.compileExitFromNativeCode(unreachableStatus)
 
 					// Emit code for .then label.
-					skip := compiler.compileLabel(wazeroir.OperationLabel{Label: thenBranchTarget.Target.Label})
+					skip := compiler.compileLabel(wazeroir.OperationLabel{Label: thenBranchTarget.Target})
 					require.False(t, skip)
 					compiler.compileExitFromNativeCode(thenLabelExitStatus)
 
 					// Emit code for .else label.
-					skip = compiler.compileLabel(wazeroir.OperationLabel{Label: elseBranchTarget.Target.Label})
+					skip = compiler.compileLabel(wazeroir.OperationLabel{Label: elseBranchTarget.Target})
 					require.False(t, skip)
 					compiler.compileExitFromNativeCode(elseLabelExitStatus)
 
@@ -281,8 +281,8 @@ func TestCompiler_compileBrTable(t *testing.T) {
 	requireRunAndExpectedValueReturned := func(t *testing.T, env *compilerEnv, c compilerImpl, expValue uint32) {
 		// Emit code for each label which returns the frame ID.
 		for returnValue := uint32(0); returnValue < 7; returnValue++ {
-			label := &wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: returnValue}
-			err := c.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: label}})
+			label := wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: returnValue}
+			err := c.compileBr(wazeroir.OperationBr{Target: label})
 			require.NoError(t, err)
 			_ = c.compileLabel(wazeroir.OperationLabel{Label: label})
 			_ = c.compileConstI32(wazeroir.OperationConstI32{Value: label.FrameID})
@@ -302,9 +302,7 @@ func TestCompiler_compileBrTable(t *testing.T) {
 
 	getBranchTargetDropFromFrameID := func(frameid uint32) *wazeroir.BranchTargetDrop {
 		return &wazeroir.BranchTargetDrop{
-			Target: &wazeroir.BranchTarget{
-				Label: &wazeroir.Label{FrameID: frameid, Kind: wazeroir.LabelKindHeader},
-			},
+			Target: wazeroir.Label{FrameID: frameid, Kind: wazeroir.LabelKindHeader},
 		}
 	}
 
@@ -465,7 +463,7 @@ func TestCompiler_compileBr(t *testing.T) {
 		require.NoError(t, err)
 
 		// Branch into nil label is interpreted as return. See BranchTarget.IsReturnTarget
-		err = compiler.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: nil}})
+		err = compiler.compileBr(wazeroir.OperationBr{Target: wazeroir.Label{Kind: wazeroir.LabelKindReturn}})
 		require.NoError(t, err)
 
 		// Compile and execute the code under test.
@@ -483,16 +481,16 @@ func TestCompiler_compileBr(t *testing.T) {
 		require.NoError(t, err)
 
 		// Emit the forward br, meaning that handle Br instruction where the target label hasn't been compiled yet.
-		forwardLabel := &wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 0}
-		err = compiler.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: forwardLabel}})
+		forwardLabel := wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 0}
+		err = compiler.compileBr(wazeroir.OperationBr{Target: forwardLabel})
 		require.NoError(t, err)
 
 		// We must not reach the code after Br, so emit the code exiting with Unreachable status.
 		compiler.compileExitFromNativeCode(nativeCallStatusCodeUnreachable)
 		require.NoError(t, err)
 
-		exitLabel := &wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 1}
-		err = compiler.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: exitLabel}})
+		exitLabel := wazeroir.Label{Kind: wazeroir.LabelKindHeader, FrameID: 1}
+		err = compiler.compileBr(wazeroir.OperationBr{Target: exitLabel})
 		require.NoError(t, err)
 
 		// Emit code for the exitLabel.
@@ -504,7 +502,7 @@ func TestCompiler_compileBr(t *testing.T) {
 		// Emit code for the forwardLabel.
 		skip = compiler.compileLabel(wazeroir.OperationLabel{Label: forwardLabel})
 		require.False(t, skip)
-		err = compiler.compileBr(wazeroir.OperationBr{Target: &wazeroir.BranchTarget{Label: exitLabel}})
+		err = compiler.compileBr(wazeroir.OperationBr{Target: exitLabel})
 		require.NoError(t, err)
 
 		code, _, err := compiler.compile()
diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go
index cd45dd84..adbd1dd5 100644
--- a/internal/wazeroir/compiler_test.go
+++ b/internal/wazeroir/compiler_test.go
@@ -49,7 +49,7 @@ func TestCompile(t *testing.T) {
 			},
 			expected: &CompilationResult{
 				Operations: []Operation{ // begin with params: []
-					OperationBr{Target: &BranchTarget{}}, // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Functions:    []uint32{0},
@@ -68,7 +68,7 @@ func TestCompile(t *testing.T) {
 			expected: &CompilationResult{
 				IsHostFunction: true,
 				Operations: []Operation{ // begin with params: []
-					OperationBr{Target: &BranchTarget{}}, // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Functions:    []uint32{0},
@@ -106,7 +106,7 @@ func TestCompile(t *testing.T) {
 				Operations: []Operation{ // begin with params: [$x]
 					OperationPick{Depth: 0},                                 // [$x, $x]
 					OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$x]
-					OperationBr{Target: &BranchTarget{}},                    // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},       // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Types: []*wasm.FunctionType{
@@ -142,7 +142,7 @@ func TestCompile(t *testing.T) {
 					OperationConstI32{Value: 8}, // [8]
 					OperationLoad{Type: UnsignedTypeI32, Arg: MemoryArg{Alignment: 2, Offset: 0}}, // [x]
 					OperationDrop{Depth: &InclusiveRange{}},                                       // []
-					OperationBr{Target: &BranchTarget{}},                                          // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},                             // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Types:        []*wasm.FunctionType{v_v},
@@ -170,7 +170,7 @@ func TestCompile(t *testing.T) {
 					OperationConstI32{Value: 8}, // [8]
 					OperationLoad{Type: UnsignedTypeI32, Arg: MemoryArg{Alignment: 2, Offset: 0}}, // [x]
 					OperationDrop{Depth: &InclusiveRange{}},                                       // []
-					OperationBr{Target: &BranchTarget{}},                                          // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},                             // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Types:        []*wasm.FunctionType{v_v},
@@ -194,7 +194,7 @@ func TestCompile(t *testing.T) {
 					OperationPick{Depth: 0}, // [$delta, $delta]
 					OperationMemoryGrow{},   // [$delta, $old_size]
 					OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$old_size]
-					OperationBr{Target: &BranchTarget{}},                    // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},       // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Types: []*wasm.FunctionType{{
@@ -267,14 +267,12 @@ func TestCompile_Block(t *testing.T) {
 			expected: &CompilationResult{
 				Operations: []Operation{ // begin with params: []
 					OperationBr{
-						Target: &BranchTarget{
-							Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
-						},
+						Target: Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
 					},
 					OperationLabel{
-						Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
+						Label: Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
 					},
-					OperationBr{Target: &BranchTarget{}}, // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},
 				},
 				// Note: i32.add comes after br 0 so is unreachable. Compilation succeeds when it feels like it
 				// shouldn't because the br instruction is stack-polymorphic. In other words, (br 0) substitutes for the
@@ -349,12 +347,12 @@ func TestCompile_BulkMemoryOperations(t *testing.T) {
 
 	expected := &CompilationResult{
 		Operations: []Operation{ // begin with params: []
-			OperationConstI32{16},                // [16]
-			OperationConstI32{0},                 // [16, 0]
-			OperationConstI32{7},                 // [16, 0, 7]
-			OperationMemoryInit{1},               // []
-			OperationDataDrop{1},                 // []
-			OperationBr{Target: &BranchTarget{}}, // return!
+			OperationConstI32{16},                             // [16]
+			OperationConstI32{0},                              // [16, 0]
+			OperationConstI32{7},                              // [16, 0, 7]
+			OperationMemoryInit{1},                            // []
+			OperationDataDrop{1},                              // []
+			OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 		},
 		HasMemory:        true,
 		UsesMemory:       true,
@@ -405,7 +403,7 @@ func TestCompile_MultiValue(t *testing.T) {
 					OperationPick{Depth: 0},                                 // [$x, $y, $y]
 					OperationPick{Depth: 2},                                 // [$x, $y, $y, $x]
 					OperationDrop{Depth: &InclusiveRange{Start: 2, End: 3}}, // [$y, $x]
-					OperationBr{Target: &BranchTarget{}},                    // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}},       // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Signature:    i32i32_i32i32,
@@ -441,14 +439,12 @@ func TestCompile_MultiValue(t *testing.T) {
 					OperationConstF64{Value: 4}, // [4]
 					OperationConstF64{Value: 5}, // [4, 5]
 					OperationBr{
-						Target: &BranchTarget{
-							Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
-						},
+						Target: Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
 					},
 					OperationLabel{
-						Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
+						Label: Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
 					},
-					OperationBr{Target: &BranchTarget{}}, // return!
+					OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 				},
 				// Note: f64.add comes after br 0 so is unreachable. This is why neither the add, nor its other operand
 				// are in the above compilation result.
@@ -471,9 +467,9 @@ func TestCompile_MultiValue(t *testing.T) {
 			},
 			expected: &CompilationResult{
 				Operations: []Operation{ // begin with params: []
-					OperationConstI32{Value: 306},        // [306]
-					OperationConstI64{Value: 356},        // [306, 356]
-					OperationBr{Target: &BranchTarget{}}, // return!
+					OperationConstI32{Value: 306},                     // [306]
+					OperationConstI64{Value: 356},                     // [306, 356]
+					OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 				},
 				LabelCallers: map[string]uint32{},
 				Signature:    _i32i64,
@@ -509,20 +505,20 @@ func TestCompile_MultiValue(t *testing.T) {
 					OperationConstI32{Value: 1}, // [$0, 1]
 					OperationPick{Depth: 1},     // [$0, 1, $0]
 					OperationBrIf{ // [$0, 1]
-						Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
-						Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
+						Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}},
+						Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}},
 					},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindHeader}},
 					OperationConstI32{Value: 2},         // [$0, 1, 2]
 					OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindElse}},
 					OperationConstI32{Value: uint32(api.EncodeI32(-2))}, // [$0, 1, -2]
 					OperationAdd{Type: UnsignedTypeI32},                 // [$0, -1]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindContinuation}},
 					OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
-					OperationBr{Target: &BranchTarget{}},
+					OperationBr{Target: Label{Kind: LabelKindReturn}},
 				},
 				LabelCallers: map[string]uint32{
 					".L2":      1,
@@ -568,18 +564,18 @@ func TestCompile_MultiValue(t *testing.T) {
 					OperationConstI32{Value: 2}, // [$0, 1, 2]
 					OperationPick{Depth: 2},     // [$0, 1, 2, $0]
 					OperationBrIf{ // [$0, 1, 2]
-						Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
-						Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
+						Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}},
+						Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}},
 					},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindHeader}},
 					OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindElse}},
 					OperationSub{Type: UnsignedTypeI32}, // [$0, -1]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindContinuation}},
 					OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
-					OperationBr{Target: &BranchTarget{}},
+					OperationBr{Target: Label{Kind: LabelKindReturn}},
 				},
 				LabelCallers: map[string]uint32{
 					".L2":      1,
@@ -625,18 +621,18 @@ func TestCompile_MultiValue(t *testing.T) {
 					OperationConstI32{Value: 2}, // [$0, 1, 2]
 					OperationPick{Depth: 2},     // [$0, 1, 2, $0]
 					OperationBrIf{ // [$0, 1, 2]
-						Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
-						Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
+						Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}},
+						Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}},
 					},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindHeader}},
 					OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindElse}},
 					OperationSub{Type: UnsignedTypeI32}, // [$0, -1]
-					OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
-					OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationBr{Target: Label{FrameID: 2, Kind: LabelKindContinuation}},
+					OperationLabel{Label: Label{FrameID: 2, Kind: LabelKindContinuation}},
 					OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
-					OperationBr{Target: &BranchTarget{}},
+					OperationBr{Target: Label{Kind: LabelKindReturn}},
 				},
 				LabelCallers: map[string]uint32{
 					".L2":      1,
@@ -689,7 +685,7 @@ func TestCompile_NonTrappingFloatToIntConversion(t *testing.T) {
 				NonTrapping: true,
 			},
 			OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [i32.trunc_sat_f32_s($0)]
-			OperationBr{Target: &BranchTarget{}},                    // return!
+			OperationBr{Target: Label{Kind: LabelKindReturn}},       // return!
 		},
 		LabelCallers: map[string]uint32{},
 		Signature:    f32_i32,
@@ -720,7 +716,7 @@ func TestCompile_SignExtensionOps(t *testing.T) {
 			OperationPick{Depth: 0},                                 // [$0, $0]
 			OperationSignExtend32From8{},                            // [$0, i32.extend8_s($0)]
 			OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [i32.extend8_s($0)]
-			OperationBr{Target: &BranchTarget{}},                    // return!
+			OperationBr{Target: Label{Kind: LabelKindReturn}},       // return!
 		},
 		LabelCallers: map[string]uint32{},
 		Signature:    i32_i32,
@@ -770,7 +766,7 @@ func TestCompile_CallIndirectNonZeroTableIndex(t *testing.T) {
 		Operations: []Operation{ // begin with params: []
 			OperationConstI32{},
 			OperationCallIndirect{TypeIndex: 2, TableIndex: 5},
-			OperationBr{Target: &BranchTarget{}}, // return!
+			OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 		},
 		HasTable:     true,
 		LabelCallers: map[string]uint32{},
@@ -803,7 +799,7 @@ func TestCompile_Refs(t *testing.T) {
 			expected: []Operation{
 				OperationRefFunc{FunctionIndex: 100},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -816,7 +812,7 @@ func TestCompile_Refs(t *testing.T) {
 			expected: []Operation{
 				OperationConstI64{Value: 0},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -829,7 +825,7 @@ func TestCompile_Refs(t *testing.T) {
 			expected: []Operation{
 				OperationConstI64{Value: 0},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -844,7 +840,7 @@ func TestCompile_Refs(t *testing.T) {
 				OperationRefFunc{FunctionIndex: 100},
 				OperationEqz{Type: UnsignedInt64},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -859,7 +855,7 @@ func TestCompile_Refs(t *testing.T) {
 				OperationConstI64{Value: 0},
 				OperationEqz{Type: UnsignedInt64},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 	}
@@ -897,7 +893,7 @@ func TestCompile_TableGetOrSet(t *testing.T) {
 				OperationConstI32{Value: 10},
 				OperationTableGet{TableIndex: 0},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -912,7 +908,7 @@ func TestCompile_TableGetOrSet(t *testing.T) {
 				OperationConstI32{Value: 10},
 				OperationConstI64{Value: 0},
 				OperationTableSet{TableIndex: 0},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -927,7 +923,7 @@ func TestCompile_TableGetOrSet(t *testing.T) {
 				OperationConstI32{Value: 10},
 				OperationRefFunc{FunctionIndex: 1},
 				OperationTableSet{TableIndex: 0},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 	}
@@ -967,7 +963,7 @@ func TestCompile_TableGrowFillSize(t *testing.T) {
 				OperationConstI32{Value: 1},
 				OperationTableGrow{TableIndex: 1},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -984,7 +980,7 @@ func TestCompile_TableGrowFillSize(t *testing.T) {
 				OperationConstI64{Value: 0}, // Null ref.
 				OperationConstI32{Value: 1},
 				OperationTableFill{TableIndex: 1},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -996,7 +992,7 @@ func TestCompile_TableGrowFillSize(t *testing.T) {
 			expected: []Operation{
 				OperationTableSize{TableIndex: 1},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 	}
@@ -1037,7 +1033,7 @@ func TestCompile_Locals(t *testing.T) {
 			expected: []Operation{
 				OperationPick{Depth: 1, IsTargetVector: true}, // [param[0].low, param[0].high] -> [param[0].low, param[0].high, param[0].low, param[0].high]
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1053,7 +1049,7 @@ func TestCompile_Locals(t *testing.T) {
 			expected: []Operation{
 				OperationPick{Depth: 0, IsTargetVector: false}, // [param[0]] -> [param[0], param[0]]
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1073,7 +1069,7 @@ func TestCompile_Locals(t *testing.T) {
 				OperationV128Const{Lo: 0, Hi: 0},
 				OperationPick{Depth: 1, IsTargetVector: true}, // [p[0].low, p[0].high] -> [p[0].low, p[0].high, p[0].low, p[0].high]
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1095,7 +1091,7 @@ func TestCompile_Locals(t *testing.T) {
 				// [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02]
 				OperationSet{Depth: 3, IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1113,7 +1109,7 @@ func TestCompile_Locals(t *testing.T) {
 				OperationConstI32{Value: 0x1},
 				OperationSet{Depth: 1, IsTargetVector: false},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1139,7 +1135,7 @@ func TestCompile_Locals(t *testing.T) {
 				// [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02]
 				OperationSet{Depth: 3, IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1163,7 +1159,7 @@ func TestCompile_Locals(t *testing.T) {
 				// [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] -> [0x01, 0x02, 0x01, 0x02]
 				OperationSet{Depth: 5, IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1182,7 +1178,7 @@ func TestCompile_Locals(t *testing.T) {
 				OperationPick{Depth: 0, IsTargetVector: false},
 				OperationSet{Depth: 2, IsTargetVector: false},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 		{
@@ -1210,7 +1206,7 @@ func TestCompile_Locals(t *testing.T) {
 				// [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x2] -> [0x01, 0x02, 0x01, 0x02]
 				OperationSet{Depth: 5, IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
-				OperationBr{Target: &BranchTarget{}}, // return!
+				OperationBr{Target: Label{Kind: LabelKindReturn}}, // return!
 			},
 		},
 	}
@@ -2833,7 +2829,7 @@ func TestCompile_unreachable_Br_BrIf_BrTable(t *testing.T) {
 					wasm.OpcodeEnd, // End the function.
 				}}},
 			},
-			expected: []Operation{OperationBr{Target: &BranchTarget{}}},
+			expected: []Operation{OperationBr{Target: Label{Kind: LabelKindReturn}}},
 		},
 		{
 			name: "br_if",
@@ -2849,7 +2845,7 @@ func TestCompile_unreachable_Br_BrIf_BrTable(t *testing.T) {
 					wasm.OpcodeEnd, // End the function.
 				}}},
 			},
-			expected: []Operation{OperationBr{Target: &BranchTarget{}}},
+			expected: []Operation{OperationBr{Target: Label{Kind: LabelKindReturn}}},
 		},
 		{
 			name: "br_table",
@@ -2864,7 +2860,7 @@ func TestCompile_unreachable_Br_BrIf_BrTable(t *testing.T) {
 					wasm.OpcodeEnd, // End the function.
 				}}},
 			},
-			expected: []Operation{OperationBr{Target: &BranchTarget{}}},
+			expected: []Operation{OperationBr{Target: Label{Kind: LabelKindReturn}}},
 		},
 	}
 
@@ -2903,7 +2899,7 @@ func TestCompile_drop_vectors(t *testing.T) {
 				// InclusiveRange is the range in uint64 representation, so dropping a vector value on top
 				// should be translated as drop [0..1] inclusively.
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}},
+				OperationBr{Target: Label{Kind: LabelKindReturn}},
 			},
 		},
 	}
@@ -2947,7 +2943,7 @@ func TestCompile_select_vectors(t *testing.T) {
 				OperationConstI32{Value: 0},
 				OperationSelect{IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}},
+				OperationBr{Target: Label{Kind: LabelKindReturn}},
 			},
 		},
 		{
@@ -2973,7 +2969,7 @@ func TestCompile_select_vectors(t *testing.T) {
 				OperationConstI32{Value: 0},
 				OperationSelect{IsTargetVector: true},
 				OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
-				OperationBr{Target: &BranchTarget{}},
+				OperationBr{Target: Label{Kind: LabelKindReturn}},
 			},
 		},
 	}