diff --git a/core/vm/contract.go b/core/vm/contract.go
index 915193d137b3e14791eb71ce3c84beb79e0ceccb..61dbd5007adb21138267d6225309b31707a769e1 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -96,19 +96,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
 	return c.isCode(udest)
 }
 
-func (c *Contract) validJumpSubdest(udest uint64) bool {
-	// PC cannot go beyond len(code) and certainly can't be bigger than 63 bits.
-	// Don't bother checking for BEGINSUB in that case.
-	if int64(udest) < 0 || udest >= uint64(len(c.Code)) {
-		return false
-	}
-	// Only BEGINSUBs allowed for destinations
-	if OpCode(c.Code[udest]) != BEGINSUB {
-		return false
-	}
-	return c.isCode(udest)
-}
-
 // isCode returns true if the provided PC location is an actual opcode, as
 // opposed to a data-segment following a PUSHN operation.
 func (c *Contract) isCode(udest uint64) bool {
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 962c0f14b1624924dd31252ce217c9b06975a4d6..0c8bf1792e9dbca633cc2fe27867a0b700da6ac6 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -29,7 +29,6 @@ var activators = map[int]func(*JumpTable){
 	2200: enable2200,
 	1884: enable1884,
 	1344: enable1344,
-	2315: enable2315,
 }
 
 // EnableEIP enables the given EIP on the config.
@@ -108,34 +107,6 @@ func enable2200(jt *JumpTable) {
 	jt[SSTORE].dynamicGas = gasSStoreEIP2200
 }
 
-// enable2315 applies EIP-2315 (Simple Subroutines)
-// - Adds opcodes that jump to and return from subroutines
-func enable2315(jt *JumpTable) {
-	// New opcode
-	jt[BEGINSUB] = &operation{
-		execute:     opBeginSub,
-		constantGas: GasQuickStep,
-		minStack:    minStack(0, 0),
-		maxStack:    maxStack(0, 0),
-	}
-	// New opcode
-	jt[JUMPSUB] = &operation{
-		execute:     opJumpSub,
-		constantGas: GasSlowStep,
-		minStack:    minStack(1, 0),
-		maxStack:    maxStack(1, 0),
-		jumps:       true,
-	}
-	// New opcode
-	jt[RETURNSUB] = &operation{
-		execute:     opReturnSub,
-		constantGas: GasFastStep,
-		minStack:    minStack(0, 0),
-		maxStack:    maxStack(0, 0),
-		jumps:       true,
-	}
-}
-
 // enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
 // https://eips.ethereum.org/EIPS/eip-2929
 func enable2929(jt *JumpTable) {
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 44da014de919fb91e63eb27071a151884d8cf02f..ac04afe8b7530b107de94bce1bbe57814b099432 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -45,12 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
 			enc.Stack[k] = (*math.HexOrDecimal256)(v)
 		}
 	}
-	if s.ReturnStack != nil {
-		enc.ReturnStack = make([]math.HexOrDecimal64, len(s.ReturnStack))
-		for k, v := range s.ReturnStack {
-			enc.ReturnStack[k] = math.HexOrDecimal64(v)
-		}
-	}
 	enc.ReturnData = s.ReturnData
 	enc.Storage = s.Storage
 	enc.Depth = s.Depth
@@ -71,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
 		Memory        *hexutil.Bytes              `json:"memory"`
 		MemorySize    *int                        `json:"memSize"`
 		Stack         []*math.HexOrDecimal256     `json:"stack"`
-		ReturnStack   []math.HexOrDecimal64       `json:"returnStack"`
 		ReturnData    *hexutil.Bytes              `json:"returnData"`
 		Storage       map[common.Hash]common.Hash `json:"-"`
 		Depth         *int                        `json:"depth"`
@@ -106,12 +99,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
 			s.Stack[k] = (*big.Int)(v)
 		}
 	}
-	if dec.ReturnStack != nil {
-		s.ReturnStack = make([]uint32, len(dec.ReturnStack))
-		for k, v := range dec.ReturnStack {
-			s.ReturnStack[k] = uint32(v)
-		}
-	}
 	if dec.ReturnData != nil {
 		s.ReturnData = *dec.ReturnData
 	}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 1137505292f1affe41ddcbcf4e41a4c7b78bdbc3..f4ca0603ed2ccfd67218ef35b35f103d108e7443 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -547,38 +547,6 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
 	return nil, nil
 }
 
-func opBeginSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	return nil, ErrInvalidSubroutineEntry
-}
-
-func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	if len(callContext.rstack.data) >= 1023 {
-		return nil, ErrReturnStackExceeded
-	}
-	pos := callContext.stack.pop()
-	if !pos.IsUint64() {
-		return nil, ErrInvalidJump
-	}
-	posU64 := pos.Uint64()
-	if !callContext.contract.validJumpSubdest(posU64) {
-		return nil, ErrInvalidJump
-	}
-	callContext.rstack.push(uint32(*pc))
-	*pc = posU64 + 1
-	return nil, nil
-}
-
-func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	if len(callContext.rstack.data) == 0 {
-		return nil, ErrInvalidRetsub
-	}
-	// Other than the check that the return stack is not empty, there is no
-	// need to validate the pc from 'returns', since we only ever push valid
-	//values onto it via jumpsub.
-	*pc = uint64(callContext.rstack.pop()) + 1
-	return nil, nil
-}
-
 func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	callContext.stack.push(new(uint256.Int).SetUint64(*pc))
 	return nil, nil
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 985d5a5156c3db7baf1a6f04a5d35bd4dc5bd8b4..55d876581c87b971a1ee66de268d42cd35becd0a 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -94,7 +94,6 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
 	var (
 		env            = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = newstack()
-		rstack         = newReturnStack()
 		pc             = uint64(0)
 		evmInterpreter = env.interpreter.(*EVMInterpreter)
 	)
@@ -105,7 +104,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
 		expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
 		stack.push(x)
 		stack.push(y)
-		opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
+		opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
 		if len(stack.data) != 1 {
 			t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
 		}
@@ -220,7 +219,7 @@ func TestAddMod(t *testing.T) {
 		stack.push(z)
 		stack.push(y)
 		stack.push(x)
-		opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil})
+		opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
 		actual := stack.pop()
 		if actual.Cmp(expected) != 0 {
 			t.Errorf("Testcase %d, expected  %x, got %x", i, expected, actual)
@@ -231,10 +230,10 @@ func TestAddMod(t *testing.T) {
 // getResult is a convenience function to generate the expected values
 func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
 	var (
-		env           = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
-		stack, rstack = newstack(), newReturnStack()
-		pc            = uint64(0)
-		interpreter   = env.interpreter.(*EVMInterpreter)
+		env         = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+		stack       = newstack()
+		pc          = uint64(0)
+		interpreter = env.interpreter.(*EVMInterpreter)
 	)
 	result := make([]TwoOperandTestcase, len(args))
 	for i, param := range args {
@@ -242,7 +241,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
 		y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
 		stack.push(x)
 		stack.push(y)
-		opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
+		opFn(&pc, interpreter, &callCtx{nil, stack, nil})
 		actual := stack.pop()
 		result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
 	}
@@ -282,7 +281,7 @@ func TestJsonTestcases(t *testing.T) {
 func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 	var (
 		env            = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
-		stack, rstack  = newstack(), newReturnStack()
+		stack          = newstack()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
 
@@ -300,7 +299,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 			a.SetBytes(arg)
 			stack.push(a)
 		}
-		op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
+		op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
 		stack.pop()
 	}
 }
@@ -516,7 +515,7 @@ func BenchmarkOpIsZero(b *testing.B) {
 func TestOpMstore(t *testing.T) {
 	var (
 		env            = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
-		stack, rstack  = newstack(), newReturnStack()
+		stack          = newstack()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -526,12 +525,12 @@ func TestOpMstore(t *testing.T) {
 	pc := uint64(0)
 	v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
 	stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
-	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
+	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
 		t.Fatalf("Mstore fail, got %v, expected %v", got, v)
 	}
 	stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
-	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
+	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
 		t.Fatalf("Mstore failed to overwrite previous value")
 	}
@@ -540,7 +539,7 @@ func TestOpMstore(t *testing.T) {
 func BenchmarkOpMstore(bench *testing.B) {
 	var (
 		env            = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
-		stack, rstack  = newstack(), newReturnStack()
+		stack          = newstack()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -554,14 +553,14 @@ func BenchmarkOpMstore(bench *testing.B) {
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
 		stack.pushN(*value, *memStart)
-		opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
+		opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	}
 }
 
 func BenchmarkOpSHA3(bench *testing.B) {
 	var (
 		env            = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
-		stack, rstack  = newstack(), newReturnStack()
+		stack          = newstack()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -573,7 +572,7 @@ func BenchmarkOpSHA3(bench *testing.B) {
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
 		stack.pushN(*uint256.NewInt().SetUint64(32), *start)
-		opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
+		opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	}
 }
 
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 0084b7d071eee25e0804dfd307639e1d7bc9a31a..06a3b962b21d4b7cded5878c02e0d26064a4c0ad 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -67,7 +67,6 @@ type Interpreter interface {
 type callCtx struct {
 	memory   *Memory
 	stack    *Stack
-	rstack   *ReturnStack
 	contract *Contract
 }
 
@@ -161,14 +160,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 	}
 
 	var (
-		op          OpCode             // current opcode
-		mem         = NewMemory()      // bound memory
-		stack       = newstack()       // local stack
-		returns     = newReturnStack() // local returns stack
+		op          OpCode        // current opcode
+		mem         = NewMemory() // bound memory
+		stack       = newstack()  // local stack
 		callContext = &callCtx{
 			memory:   mem,
 			stack:    stack,
-			rstack:   returns,
 			contract: contract,
 		}
 		// For optimisation reason we're using uint64 as the program counter.
@@ -187,7 +184,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 	// they are returned to the pools
 	defer func() {
 		returnStack(stack)
-		returnRStack(returns)
 	}()
 	contract.Input = input
 
@@ -195,9 +191,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		defer func() {
 			if err != nil {
 				if !logged {
-					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
+					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err)
 				} else {
-					in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
+					in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
 				}
 			}
 		}()
@@ -279,7 +275,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		}
 
 		if in.cfg.Debug {
-			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
+			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err)
 			logged = true
 		}
 
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index d831f9300f853cd985ffa90ab263762281e32d2e..7b61762456d5bab7f7a341bb938f5f217b9dbc09 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -66,7 +66,6 @@ type JumpTable [256]*operation
 // contantinople, istanbul, petersburg and berlin instructions.
 func newBerlinInstructionSet() JumpTable {
 	instructionSet := newIstanbulInstructionSet()
-	enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315
 	enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
 	return instructionSet
 }
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 962be6ec8e0e30c634ecaa6c1513259974e6ddfe..41ce00ed014e7e65f5174f672f34734e76bb0e4c 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -70,7 +70,6 @@ type StructLog struct {
 	Memory        []byte                      `json:"memory"`
 	MemorySize    int                         `json:"memSize"`
 	Stack         []*big.Int                  `json:"stack"`
-	ReturnStack   []uint32                    `json:"returnStack"`
 	ReturnData    []byte                      `json:"returnData"`
 	Storage       map[common.Hash]common.Hash `json:"-"`
 	Depth         int                         `json:"depth"`
@@ -81,7 +80,6 @@ type StructLog struct {
 // overrides for gencodec
 type structLogMarshaling struct {
 	Stack       []*math.HexOrDecimal256
-	ReturnStack []math.HexOrDecimal64
 	Gas         math.HexOrDecimal64
 	GasCost     math.HexOrDecimal64
 	Memory      hexutil.Bytes
@@ -110,8 +108,8 @@ func (s *StructLog) ErrorString() string {
 // if you need to retain them beyond the current call.
 type Tracer interface {
 	CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
-	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error
-	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
+	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error
+	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
 	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
 }
 
@@ -148,7 +146,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
 // CaptureState logs a new structured log message and pushes it out to the environment
 //
 // CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
 	// check if already accumulated the specified number of logs
 	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
 		return errTraceLimitReached
@@ -167,11 +165,6 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 			stck[i] = new(big.Int).Set(item.ToBig())
 		}
 	}
-	var rstack []uint32
-	if !l.cfg.DisableStack && rStack != nil {
-		rstck := make([]uint32, len(rStack.data))
-		copy(rstck, rStack.data)
-	}
 	// Copy a snapshot of the current storage to a new container
 	var storage Storage
 	if !l.cfg.DisableStorage {
@@ -204,14 +197,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 		copy(rdata, rData)
 	}
 	// create a new snapshot of the EVM.
-	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err}
+	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err}
 	l.logs = append(l.logs, log)
 	return nil
 }
 
 // CaptureFault implements the Tracer interface to trace an execution fault
 // while running an opcode.
-func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
 	return nil
 }
 
@@ -252,12 +245,6 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
 				fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
 			}
 		}
-		if len(log.ReturnStack) > 0 {
-			fmt.Fprintln(writer, "ReturnStack:")
-			for i := len(log.Stack) - 1; i >= 0; i-- {
-				fmt.Fprintf(writer, "%08d  0x%x (%d)\n", len(log.Stack)-i-1, log.ReturnStack[i], log.ReturnStack[i])
-			}
-		}
 		if len(log.Memory) > 0 {
 			fmt.Fprintln(writer, "Memory:")
 			fmt.Fprint(writer, hex.Dump(log.Memory))
@@ -323,7 +310,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
 	return nil
 }
 
-func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
+func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
 	fmt.Fprintf(t.out, "| %4d  | %10v  |  %3d |", pc, op, cost)
 
 	if !t.cfg.DisableStack {
@@ -334,14 +321,6 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
 		}
 		b := fmt.Sprintf("[%v]", strings.Join(a, ","))
 		fmt.Fprintf(t.out, "%10v |", b)
-
-		// format return stack
-		a = a[:0]
-		for _, elem := range rStack.data {
-			a = append(a, fmt.Sprintf("%2d", elem))
-		}
-		b = fmt.Sprintf("[%v]", strings.Join(a, ","))
-		fmt.Fprintf(t.out, "%10v |", b)
 	}
 	fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
 	fmt.Fprintln(t.out, "")
@@ -351,7 +330,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
 	return nil
 }
 
-func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
 
 	fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
 
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 5f3f2c42f786d69130382b37884451d4229531f2..a27c261ed8167968ff69f22f55666f3905246338 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
 }
 
 // CaptureState outputs state information on the logger.
-func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
 	log := StructLog{
 		Pc:            pc,
 		Op:            op,
@@ -68,7 +68,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
 			logstack[i] = item.ToBig()
 		}
 		log.Stack = logstack
-		log.ReturnStack = rStack.data
 	}
 	if !l.cfg.DisableReturnData {
 		log.ReturnData = rData
@@ -77,7 +76,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
 }
 
 // CaptureFault outputs state information on the logger.
-func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
 	return nil
 }
 
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index bf7d5358f8968ac310502dcd9d7f498686bef2b7..5a5f42fd34802c9884d198165c0d7165ee657b1b 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -55,13 +55,12 @@ func TestStoreCapture(t *testing.T) {
 		logger   = NewStructLogger(nil)
 		mem      = NewMemory()
 		stack    = newstack()
-		rstack   = newReturnStack()
 		contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
 	)
 	stack.push(uint256.NewInt().SetUint64(1))
 	stack.push(uint256.NewInt())
 	var index common.Hash
-	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil)
+	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, nil, contract, 0, nil)
 	if len(logger.storage[contract.Address()]) == 0 {
 		t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
 	}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index da7b2ee4aa17b28c83d80d99a0416c7cf831d51c..b0adf37d0c2698b788a2c2af7b2be0f6270ed04f 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -107,21 +107,18 @@ const (
 
 // 0x50 range - 'storage' and execution.
 const (
-	POP       OpCode = 0x50
-	MLOAD     OpCode = 0x51
-	MSTORE    OpCode = 0x52
-	MSTORE8   OpCode = 0x53
-	SLOAD     OpCode = 0x54
-	SSTORE    OpCode = 0x55
-	JUMP      OpCode = 0x56
-	JUMPI     OpCode = 0x57
-	PC        OpCode = 0x58
-	MSIZE     OpCode = 0x59
-	GAS       OpCode = 0x5a
-	JUMPDEST  OpCode = 0x5b
-	BEGINSUB  OpCode = 0x5c
-	RETURNSUB OpCode = 0x5d
-	JUMPSUB   OpCode = 0x5e
+	POP      OpCode = 0x50
+	MLOAD    OpCode = 0x51
+	MSTORE   OpCode = 0x52
+	MSTORE8  OpCode = 0x53
+	SLOAD    OpCode = 0x54
+	SSTORE   OpCode = 0x55
+	JUMP     OpCode = 0x56
+	JUMPI    OpCode = 0x57
+	PC       OpCode = 0x58
+	MSIZE    OpCode = 0x59
+	GAS      OpCode = 0x5a
+	JUMPDEST OpCode = 0x5b
 )
 
 // 0x60 range.
@@ -300,10 +297,6 @@ var opCodeToString = map[OpCode]string{
 	GAS:      "GAS",
 	JUMPDEST: "JUMPDEST",
 
-	BEGINSUB:  "BEGINSUB",
-	JUMPSUB:   "JUMPSUB",
-	RETURNSUB: "RETURNSUB",
-
 	// 0x60 range - push.
 	PUSH1:  "PUSH1",
 	PUSH2:  "PUSH2",
@@ -468,9 +461,6 @@ var stringToOp = map[string]OpCode{
 	"MSIZE":          MSIZE,
 	"GAS":            GAS,
 	"JUMPDEST":       JUMPDEST,
-	"BEGINSUB":       BEGINSUB,
-	"RETURNSUB":      RETURNSUB,
-	"JUMPSUB":        JUMPSUB,
 	"PUSH1":          PUSH1,
 	"PUSH2":          PUSH2,
 	"PUSH3":          PUSH3,
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index af69e3333fc2671571fd3211ef1efdc17a3034b6..6e0434c2ca596b521aecc90e048d1de9cd54e0a8 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -330,14 +330,14 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat
 	return nil
 }
 
-func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error {
+func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rData []byte, contract *vm.Contract, depth int, err error) error {
 	s.steps++
 	// Enable this for more output
 	//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
 	return nil
 }
 
-func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
+func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 
@@ -345,227 +345,6 @@ func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
 	return nil
 }
 
-func TestJumpSub1024Limit(t *testing.T) {
-	state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
-	address := common.HexToAddress("0x0a")
-	// Code is
-	// 0 beginsub
-	// 1 push 0
-	// 3 jumpsub
-	//
-	// The code recursively calls itself. It should error when the returns-stack
-	// grows above 1023
-	state.SetCode(address, []byte{
-		byte(vm.PUSH1), 3,
-		byte(vm.JUMPSUB),
-		byte(vm.BEGINSUB),
-		byte(vm.PUSH1), 3,
-		byte(vm.JUMPSUB),
-	})
-	tracer := stepCounter{inner: vm.NewJSONLogger(nil, os.Stdout)}
-	// Enable 2315
-	_, _, err := Call(address, nil, &Config{State: state,
-		GasLimit:    20000,
-		ChainConfig: params.AllEthashProtocolChanges,
-		EVMConfig: vm.Config{
-			ExtraEips: []int{2315},
-			Debug:     true,
-			//Tracer:    vm.NewJSONLogger(nil, os.Stdout),
-			Tracer: &tracer,
-		}})
-	exp := "return stack limit reached"
-	if err.Error() != exp {
-		t.Fatalf("expected %v, got %v", exp, err)
-	}
-	if exp, got := 2048, tracer.steps; exp != got {
-		t.Fatalf("expected %d steps, got %d", exp, got)
-	}
-}
-
-func TestReturnSubShallow(t *testing.T) {
-	state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
-	address := common.HexToAddress("0x0a")
-	// The code does returnsub without having anything on the returnstack.
-	// It should not panic, but just fail after one step
-	state.SetCode(address, []byte{
-		byte(vm.PUSH1), 5,
-		byte(vm.JUMPSUB),
-		byte(vm.RETURNSUB),
-		byte(vm.PC),
-		byte(vm.BEGINSUB),
-		byte(vm.RETURNSUB),
-		byte(vm.PC),
-	})
-	tracer := stepCounter{}
-
-	// Enable 2315
-	_, _, err := Call(address, nil, &Config{State: state,
-		GasLimit:    10000,
-		ChainConfig: params.AllEthashProtocolChanges,
-		EVMConfig: vm.Config{
-			ExtraEips: []int{2315},
-			Debug:     true,
-			Tracer:    &tracer,
-		}})
-
-	exp := "invalid retsub"
-	if err.Error() != exp {
-		t.Fatalf("expected %v, got %v", exp, err)
-	}
-	if exp, got := 4, tracer.steps; exp != got {
-		t.Fatalf("expected %d steps, got %d", exp, got)
-	}
-}
-
-// disabled -- only used for generating markdown
-func DisabledTestReturnCases(t *testing.T) {
-	cfg := &Config{
-		EVMConfig: vm.Config{
-			Debug:     true,
-			Tracer:    vm.NewMarkdownLogger(nil, os.Stdout),
-			ExtraEips: []int{2315},
-		},
-	}
-	// This should fail at first opcode
-	Execute([]byte{
-		byte(vm.RETURNSUB),
-		byte(vm.PC),
-		byte(vm.PC),
-	}, nil, cfg)
-
-	// Should also fail
-	Execute([]byte{
-		byte(vm.PUSH1), 5,
-		byte(vm.JUMPSUB),
-		byte(vm.RETURNSUB),
-		byte(vm.PC),
-		byte(vm.BEGINSUB),
-		byte(vm.RETURNSUB),
-		byte(vm.PC),
-	}, nil, cfg)
-
-	// This should complete
-	Execute([]byte{
-		byte(vm.PUSH1), 0x4,
-		byte(vm.JUMPSUB),
-		byte(vm.STOP),
-		byte(vm.BEGINSUB),
-		byte(vm.PUSH1), 0x9,
-		byte(vm.JUMPSUB),
-		byte(vm.RETURNSUB),
-		byte(vm.BEGINSUB),
-		byte(vm.RETURNSUB),
-	}, nil, cfg)
-}
-
-// DisabledTestEipExampleCases contains various testcases that are used for the
-// EIP examples
-// This test is disabled, as it's only used for generating markdown
-func DisabledTestEipExampleCases(t *testing.T) {
-	cfg := &Config{
-		EVMConfig: vm.Config{
-			Debug:     true,
-			Tracer:    vm.NewMarkdownLogger(nil, os.Stdout),
-			ExtraEips: []int{2315},
-		},
-	}
-	prettyPrint := func(comment string, code []byte) {
-		instrs := make([]string, 0)
-		it := asm.NewInstructionIterator(code)
-		for it.Next() {
-			if it.Arg() != nil && 0 < len(it.Arg()) {
-				instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg()))
-			} else {
-				instrs = append(instrs, fmt.Sprintf("%v", it.Op()))
-			}
-		}
-		ops := strings.Join(instrs, ", ")
-
-		fmt.Printf("%v\nBytecode: `0x%x` (`%v`)\n",
-			comment,
-			code, ops)
-		Execute(code, nil, cfg)
-	}
-
-	{ // First eip testcase
-		code := []byte{
-			byte(vm.PUSH1), 4,
-			byte(vm.JUMPSUB),
-			byte(vm.STOP),
-			byte(vm.BEGINSUB),
-			byte(vm.RETURNSUB),
-		}
-		prettyPrint("This should jump into a subroutine, back out and stop.", code)
-	}
-
-	{
-		code := []byte{
-			byte(vm.PUSH9), 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 4 + 8,
-			byte(vm.JUMPSUB),
-			byte(vm.STOP),
-			byte(vm.BEGINSUB),
-			byte(vm.PUSH1), 8 + 9,
-			byte(vm.JUMPSUB),
-			byte(vm.RETURNSUB),
-			byte(vm.BEGINSUB),
-			byte(vm.RETURNSUB),
-		}
-		prettyPrint("This should execute fine, going into one two depths of subroutines", code)
-	}
-	// TODO(@holiman) move this test into an actual test, which not only prints
-	// out the trace.
-	{
-		code := []byte{
-			byte(vm.PUSH9), 0x01, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 4 + 8,
-			byte(vm.JUMPSUB),
-			byte(vm.STOP),
-			byte(vm.BEGINSUB),
-			byte(vm.PUSH1), 8 + 9,
-			byte(vm.JUMPSUB),
-			byte(vm.RETURNSUB),
-			byte(vm.BEGINSUB),
-			byte(vm.RETURNSUB),
-		}
-		prettyPrint("This should fail, since the given location is outside of the "+
-			"code-range. The code is the same as previous example, except that the "+
-			"pushed location is `0x01000000000000000c` instead of `0x0c`.", code)
-	}
-	{
-		// This should fail at first opcode
-		code := []byte{
-			byte(vm.RETURNSUB),
-			byte(vm.PC),
-			byte(vm.PC),
-		}
-		prettyPrint("This should fail at first opcode, due to shallow `return_stack`", code)
-
-	}
-	{
-		code := []byte{
-			byte(vm.PUSH1), 5, // Jump past the subroutine
-			byte(vm.JUMP),
-			byte(vm.BEGINSUB),
-			byte(vm.RETURNSUB),
-			byte(vm.JUMPDEST),
-			byte(vm.PUSH1), 3, // Now invoke the subroutine
-			byte(vm.JUMPSUB),
-		}
-		prettyPrint("In this example. the JUMPSUB is on the last byte of code. When the "+
-			"subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, "+
-			"and not exit with error", code)
-	}
-
-	{
-		code := []byte{
-			byte(vm.BEGINSUB),
-			byte(vm.RETURNSUB),
-			byte(vm.STOP),
-		}
-		prettyPrint("In this example, the code 'walks' into a subroutine, which is not "+
-			"allowed, and causes an error", code)
-	}
-}
-
 // benchmarkNonModifyingCode benchmarks code, but if the code modifies the
 // state, this should not be used, since it does not reset the state between runs.
 func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
diff --git a/core/vm/stack.go b/core/vm/stack.go
index af27d6552cebdb50df72dd6e4b5eed82b05fc244..c71d2653a5cd19d8a30b664fa2898dbcdc448d15 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -98,34 +98,3 @@ func (st *Stack) Print() {
 	}
 	fmt.Println("#############")
 }
-
-var rStackPool = sync.Pool{
-	New: func() interface{} {
-		return &ReturnStack{data: make([]uint32, 0, 10)}
-	},
-}
-
-// ReturnStack is an object for basic return stack operations.
-type ReturnStack struct {
-	data []uint32
-}
-
-func newReturnStack() *ReturnStack {
-	return rStackPool.Get().(*ReturnStack)
-}
-
-func returnRStack(rs *ReturnStack) {
-	rs.data = rs.data[:0]
-	rStackPool.Put(rs)
-}
-
-func (st *ReturnStack) push(d uint32) {
-	st.data = append(st.data, d)
-}
-
-// A uint32 is sufficient as for code below 4.2G
-func (st *ReturnStack) pop() (ret uint32) {
-	ret = st.data[len(st.data)-1]
-	st.data = st.data[:len(st.data)-1]
-	return
-}
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index 80775caa8e594014d59d6e300eacf293baa3bac0..4c398edf343cc35367ebf48d168c2e29df602cc9 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -544,7 +544,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b
 }
 
 // CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error {
+func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rdata []byte, contract *vm.Contract, depth int, err error) error {
 	if jst.err == nil {
 		// Initialize the context if it wasn't done yet
 		if !jst.inited {
@@ -595,7 +595,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 
 // CaptureFault implements the Tracer interface to trace an execution fault
 // while running an opcode.
-func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
+func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
 	if jst.err == nil {
 		// Apart from the error, everything matches the previous invocation
 		jst.errorValue = new(string)
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index f28e14864bd14853e945cb6d2a63871f995b0331..d96030385cbb65544be9811bc9b6384270897529 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -152,10 +152,10 @@ func TestHaltBetweenSteps(t *testing.T) {
 	env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
 	contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
 
-	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
+	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
 	timeout := errors.New("stahp")
 	tracer.Stop(timeout)
-	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
+	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
 
 	if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
 		t.Errorf("Expected timeout error, got %v", err)