proto, runtime/protoiface, internal/impl: add fast-path Merge

Comparing -tags=protoreflect to fast-path:

name                              old time/op    new time/op    delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12    1.70µs ± 1%    0.30µs ± 1%  -82.64%  (p=0.001 n=7+7)
/Clone/google_message1_proto3-12    1.01µs ± 1%    0.19µs ± 1%  -80.77%  (p=0.000 n=7+8)
/Clone/google_message2-12            818µs ± 8%     141µs ± 6%  -82.78%  (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12               51.1ns ± 1%    39.3ns ± 3%  -23.03%  (p=0.000 n=7+8)
RepeatedInt32/Clone-12              24.5µs ± 1%     1.1µs ± 3%  -95.64%  (p=0.000 n=8+8)
Required/Clone-12                    978ns ± 1%     132ns ± 2%  -86.46%  (p=0.000 n=8+8)

name                              old alloc/op   new alloc/op   delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12    1.08kB ± 0%    0.74kB ± 0%  -31.85%  (p=0.000 n=8+8)
/Clone/google_message1_proto3-12      872B ± 0%      544B ± 0%  -37.61%  (p=0.000 n=8+8)
/Clone/google_message2-12            602kB ± 0%     411kB ± 0%  -31.65%  (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12                96.0B ± 0%     64.0B ± 0%  -33.33%  (p=0.000 n=8+8)
RepeatedInt32/Clone-12              25.4kB ± 0%     3.2kB ± 0%  -87.33%  (p=0.000 n=8+8)
Required/Clone-12                     416B ± 0%      256B ± 0%  -38.46%  (p=0.000 n=8+8)

name                              old allocs/op  new allocs/op  delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12      52.0 ± 0%      21.0 ± 0%  -59.62%  (p=0.000 n=8+8)
/Clone/google_message1_proto3-12      33.0 ± 0%       3.0 ± 0%  -90.91%  (p=0.000 n=8+8)
/Clone/google_message2-12            22.3k ± 0%      7.5k ± 0%  -66.41%  (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12                 3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.000 n=8+8)
RepeatedInt32/Clone-12               1.51k ± 0%     0.00k ± 0%  -99.80%  (p=0.000 n=8+8)
Required/Clone-12                     51.0 ± 0%      18.0 ± 0%  -64.71%  (p=0.000 n=8+8)

Change-Id: Ife9018097c34cb025dc9c4fdd9a61b2f947853c6
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/219147
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/benchmarks/bench_test.go b/internal/benchmarks/bench_test.go
index 9335dbd..33b18fc 100644
--- a/internal/benchmarks/bench_test.go
+++ b/internal/benchmarks/bench_test.go
@@ -102,6 +102,16 @@
 	})
 }
 
+func Benchmark(b *testing.B) {
+	bench(b, "Clone", func(ds dataset, pb *testing.PB) {
+		for pb.Next() {
+			for _, src := range ds.messages {
+				proto.Clone(src)
+			}
+		}
+	})
+}
+
 func bench(b *testing.B, name string, f func(dataset, *testing.PB)) {
 	b.Helper()
 	b.Run(name, func(b *testing.B) {
diff --git a/internal/benchmarks/micro/micro_test.go b/internal/benchmarks/micro/micro_test.go
index b36dc78..e4ff349 100644
--- a/internal/benchmarks/micro/micro_test.go
+++ b/internal/benchmarks/micro/micro_test.go
@@ -63,6 +63,14 @@
 			}
 		})
 	})
+	b.Run("Clone", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			m := &emptypb.Empty{}
+			for pb.Next() {
+				proto.Clone(m)
+			}
+		})
+	})
 }
 
 // BenchmarkRepeatedInt32 tests a message containing 500 non-packed repeated int32s.
@@ -116,6 +124,13 @@
 			}
 		})
 	})
+	b.Run("Clone", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			for pb.Next() {
+				proto.Clone(m)
+			}
+		})
+	})
 }
 
 // BenchmarkRequired tests a message containing a required field.
@@ -179,4 +194,11 @@
 			}
 		})
 	})
+	b.Run("Clone", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			for pb.Next() {
+				proto.Clone(m)
+			}
+		})
+	})
 }
diff --git a/internal/cmd/generate-types/impl.go b/internal/cmd/generate-types/impl.go
index 0a89144..2328de2 100644
--- a/internal/cmd/generate-types/impl.go
+++ b/internal/cmd/generate-types/impl.go
@@ -125,6 +125,7 @@
 	size:      size{{.Name}},
 	marshal:   append{{.Name}},
 	unmarshal: consume{{.Name}},
+	merge:     merge{{.GoType.PointerMethod}},
 }
 
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
@@ -160,6 +161,7 @@
 	size:      size{{.Name}},
 	marshal:   append{{.Name}}ValidateUTF8,
 	unmarshal: consume{{.Name}}ValidateUTF8,
+	merge:     merge{{.GoType.PointerMethod}},
 }
 {{end}}
 
@@ -206,6 +208,7 @@
 	size:      size{{.Name}}NoZero,
 	marshal:   append{{.Name}}NoZero,
 	unmarshal: consume{{.Name}}{{if .ToGoTypeNoZero}}NoZero{{end}},
+	merge:     merge{{.GoType.PointerMethod}}NoZero,
 }
 
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
@@ -247,6 +250,7 @@
 	size:      size{{.Name}}NoZero,
 	marshal:   append{{.Name}}NoZeroValidateUTF8,
 	unmarshal: consume{{.Name}}{{if .ToGoTypeNoZero}}NoZero{{end}}ValidateUTF8,
+	merge:     merge{{.GoType.PointerMethod}}NoZero,
 }
 {{end}}
 
@@ -291,6 +295,7 @@
 	size:      size{{.Name}}Ptr,
 	marshal:   append{{.Name}}Ptr,
 	unmarshal: consume{{.Name}}Ptr,
+	merge:     merge{{.GoType.PointerMethod}}Ptr,
 }
 {{end}}
 
@@ -356,6 +361,7 @@
 	size:      size{{.Name}}Slice,
 	marshal:   append{{.Name}}Slice,
 	unmarshal: consume{{.Name}}Slice,
+	merge:     merge{{.GoType.PointerMethod}}Slice,
 }
 
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
@@ -394,6 +400,7 @@
 	size:      size{{.Name}}Slice,
 	marshal:   append{{.Name}}SliceValidateUTF8,
 	unmarshal: consume{{.Name}}SliceValidateUTF8,
+	merge:     merge{{.GoType.PointerMethod}}Slice,
 }
 {{end}}
 
@@ -441,6 +448,7 @@
 	size:      size{{.Name}}PackedSlice,
 	marshal:   append{{.Name}}PackedSlice,
 	unmarshal: consume{{.Name}}Slice,
+	merge:     merge{{.GoType.PointerMethod}}Slice,
 }
 {{end}}
 
@@ -473,9 +481,14 @@
 }
 
 var coder{{.Name}}Value = valueCoderFuncs{
-	size:    size{{.Name}}Value,
-	marshal: append{{.Name}}Value,
+	size:      size{{.Name}}Value,
+	marshal:   append{{.Name}}Value,
 	unmarshal: consume{{.Name}}Value,
+{{- if (eq .Name "Bytes")}}
+	merge:     mergeBytesValue,
+{{- else}}
+	merge:     mergeScalarValue,
+{{- end}}
 }
 
 {{if (eq .Name "String")}}
@@ -509,6 +522,7 @@
 	size:      size{{.Name}}Value,
 	marshal:   append{{.Name}}ValueValidateUTF8,
 	unmarshal: consume{{.Name}}ValueValidateUTF8,
+	merge:     mergeScalarValue,
 }
 {{end}}
 
@@ -574,6 +588,11 @@
 	size:      size{{.Name}}SliceValue,
 	marshal:   append{{.Name}}SliceValue,
 	unmarshal: consume{{.Name}}SliceValue,
+{{- if (eq .Name "Bytes")}}
+	merge:     mergeBytesListValue,
+{{- else}}
+	merge:     mergeListValue,
+{{- end}}
 }
 
 {{if or (eq .WireType "Varint") (eq .WireType "Fixed32") (eq .WireType "Fixed64")}}
@@ -625,6 +644,7 @@
 	size:      size{{.Name}}PackedSliceValue,
 	marshal:   append{{.Name}}PackedSliceValue,
 	unmarshal: consume{{.Name}}SliceValue,
+	merge:     mergeListValue,
 }
 {{end}}
 
@@ -774,3 +794,39 @@
 
 {{end}}
 `))
+
+func generateImplMerge() string {
+	return mustExecute(implMergeTemplate, GoTypes)
+}
+
+var implMergeTemplate = template.Must(template.New("").Parse(`
+{{range .}}
+{{if ne . "[]byte"}}
+func merge{{.PointerMethod}}(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.{{.PointerMethod}}() = *src.{{.PointerMethod}}()
+}
+
+func merge{{.PointerMethod}}NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.{{.PointerMethod}}()
+	if v != {{.Zero}} {
+		*dst.{{.PointerMethod}}() = v
+	}
+}
+
+func merge{{.PointerMethod}}Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.{{.PointerMethod}}Ptr()
+	if p != nil {
+		v := *p
+		*dst.{{.PointerMethod}}Ptr() = &v
+	}
+}
+
+func merge{{.PointerMethod}}Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.{{.PointerMethod}}Slice()
+	ss := src.{{.PointerMethod}}Slice()
+	*ds = append(*ds, *ss...)
+}
+
+{{end}}
+{{end}}
+`))
diff --git a/internal/cmd/generate-types/main.go b/internal/cmd/generate-types/main.go
index 6d19508..81b210a 100644
--- a/internal/cmd/generate-types/main.go
+++ b/internal/cmd/generate-types/main.go
@@ -40,6 +40,7 @@
 	writeSource("internal/filedesc/desc_list_gen.go", generateDescListTypes())
 	writeSource("internal/impl/codec_gen.go", generateImplCodec())
 	writeSource("internal/impl/message_reflect_gen.go", generateImplMessage())
+	writeSource("internal/impl/merge_gen.go", generateImplMerge())
 	writeSource("proto/decode_gen.go", generateProtoDecode())
 	writeSource("proto/encode_gen.go", generateProtoEncode())
 	writeSource("proto/size_gen.go", generateProtoSize())
diff --git a/internal/cmd/generate-types/proto.go b/internal/cmd/generate-types/proto.go
index 0a79987..682f5f6 100644
--- a/internal/cmd/generate-types/proto.go
+++ b/internal/cmd/generate-types/proto.go
@@ -36,6 +36,18 @@
 
 type GoType string
 
+var GoTypes = []GoType{
+	GoBool,
+	GoInt32,
+	GoUint32,
+	GoInt64,
+	GoUint64,
+	GoFloat32,
+	GoFloat64,
+	GoString,
+	GoBytes,
+}
+
 const (
 	GoBool    = "bool"
 	GoInt32   = "int32"
diff --git a/internal/impl/codec_field.go b/internal/impl/codec_field.go
index dd6a8a1..98992e7 100644
--- a/internal/impl/codec_field.go
+++ b/internal/impl/codec_field.go
@@ -45,7 +45,8 @@
 		// and dispatches to the field-specific marshaler in oneofFields.
 		cf := *mi.coderFields[num]
 		ot := si.oneofWrappersByNumber[num]
-		cf.mi, cf.funcs = fieldCoder(fd, ot.Field(0).Type)
+		cf.ft = ot.Field(0).Type
+		cf.mi, cf.funcs = fieldCoder(fd, cf.ft)
 		oneofFields[ot] = &cf
 		if cf.funcs.isInit != nil {
 			needIsInit = true
@@ -92,6 +93,18 @@
 		}
 		return info.funcs.marshal(b, p, info, opts)
 	}
+	first.funcs.merge = func(dst, src pointer, _ *coderFieldInfo, opts mergeOptions) {
+		srcp, srcinfo := getInfo(src)
+		if srcinfo == nil || srcinfo.funcs.merge == nil {
+			return
+		}
+		dstp, dstinfo := getInfo(dst)
+		if dstinfo != srcinfo {
+			dst.AsValueOf(ft).Elem().Set(reflect.New(src.AsValueOf(ft).Elem().Elem().Elem().Type()))
+			dstp = pointerOfValue(dst.AsValueOf(ft).Elem().Elem()).Apply(zeroOffset)
+		}
+		srcinfo.funcs.merge(dstp, srcp, srcinfo, opts)
+	}
 	if needIsInit {
 		first.funcs.isInit = func(p pointer, _ *coderFieldInfo) error {
 			p, info := getInfo(p)
@@ -113,10 +126,9 @@
 		})
 	}
 
-	num := fd.Number()
 	return pointerCoderFuncs{
 		size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
-			m, ok := p.WeakFields().get(num)
+			m, ok := p.WeakFields().get(f.num)
 			if !ok {
 				return 0
 			}
@@ -127,7 +139,7 @@
 			return sizeMessage(m, f.tagsize, opts)
 		},
 		marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
-			m, ok := p.WeakFields().get(num)
+			m, ok := p.WeakFields().get(f.num)
 			if !ok {
 				return b, nil
 			}
@@ -139,24 +151,40 @@
 		},
 		unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			fs := p.WeakFields()
-			m, ok := fs.get(num)
+			m, ok := fs.get(f.num)
 			if !ok {
 				lazyInit()
 				if messageType == nil {
 					return unmarshalOutput{}, errUnknown
 				}
 				m = messageType.New().Interface()
-				fs.set(num, m)
+				fs.set(f.num, m)
 			}
 			return consumeMessage(b, m, wtyp, opts)
 		},
 		isInit: func(p pointer, f *coderFieldInfo) error {
-			m, ok := p.WeakFields().get(num)
+			m, ok := p.WeakFields().get(f.num)
 			if !ok {
 				return nil
 			}
 			return proto.IsInitialized(m)
 		},
+		merge: func(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+			sm, ok := src.WeakFields().get(f.num)
+			if !ok {
+				return
+			}
+			dm, ok := dst.WeakFields().get(f.num)
+			if !ok {
+				lazyInit()
+				if messageType == nil {
+					panic(fmt.Sprintf("weak message %v is not linked in", fd.Message().FullName()))
+				}
+				dm = messageType.New().Interface()
+				dst.WeakFields().set(f.num, dm)
+			}
+			opts.Merge(dm, sm)
+		},
 	}
 }
 
@@ -166,6 +194,7 @@
 			size:      sizeMessageInfo,
 			marshal:   appendMessageInfo,
 			unmarshal: consumeMessageInfo,
+			merge:     mergeMessage,
 		}
 		if needsInitCheck(mi.Desc) {
 			funcs.isInit = isInitMessageInfo
@@ -192,6 +221,7 @@
 				m := asMessage(p.AsValueOf(ft).Elem())
 				return proto.IsInitialized(m)
 			},
+			merge: mergeMessage,
 		}
 	}
 }
@@ -285,6 +315,7 @@
 	marshal:   appendMessageValue,
 	unmarshal: consumeMessageValue,
 	isInit:    isInitMessageValue,
+	merge:     mergeMessageValue,
 }
 
 func sizeGroupValue(v pref.Value, tagsize int, opts marshalOptions) int {
@@ -308,6 +339,7 @@
 	marshal:   appendGroupValue,
 	unmarshal: consumeGroupValue,
 	isInit:    isInitMessageValue,
+	merge:     mergeMessageValue,
 }
 
 func makeGroupFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
@@ -317,6 +349,7 @@
 			size:      sizeGroupType,
 			marshal:   appendGroupType,
 			unmarshal: consumeGroupType,
+			merge:     mergeMessage,
 		}
 		if needsInitCheck(mi.Desc) {
 			funcs.isInit = isInitMessageInfo
@@ -343,6 +376,7 @@
 				m := asMessage(p.AsValueOf(ft).Elem())
 				return proto.IsInitialized(m)
 			},
+			merge: mergeMessage,
 		}
 	}
 }
@@ -404,6 +438,7 @@
 			size:      sizeMessageSliceInfo,
 			marshal:   appendMessageSliceInfo,
 			unmarshal: consumeMessageSliceInfo,
+			merge:     mergeMessageSlice,
 		}
 		if needsInitCheck(mi.Desc) {
 			funcs.isInit = isInitMessageSliceInfo
@@ -423,6 +458,7 @@
 		isInit: func(p pointer, f *coderFieldInfo) error {
 			return isInitMessageSlice(p, ft)
 		},
+		merge: mergeMessageSlice,
 	}
 }
 
@@ -605,6 +641,7 @@
 	marshal:   appendMessageSliceValue,
 	unmarshal: consumeMessageSliceValue,
 	isInit:    isInitMessageSliceValue,
+	merge:     mergeMessageListValue,
 }
 
 func sizeGroupSliceValue(listv pref.Value, tagsize int, opts marshalOptions) int {
@@ -660,6 +697,7 @@
 	marshal:   appendGroupSliceValue,
 	unmarshal: consumeGroupSliceValue,
 	isInit:    isInitMessageSliceValue,
+	merge:     mergeMessageListValue,
 }
 
 func makeGroupSliceFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
@@ -669,6 +707,7 @@
 			size:      sizeGroupSliceInfo,
 			marshal:   appendGroupSliceInfo,
 			unmarshal: consumeGroupSliceInfo,
+			merge:     mergeMessageSlice,
 		}
 		if needsInitCheck(mi.Desc) {
 			funcs.isInit = isInitMessageSliceInfo
@@ -688,6 +727,7 @@
 		isInit: func(p pointer, f *coderFieldInfo) error {
 			return isInitMessageSlice(p, ft)
 		},
+		merge: mergeMessageSlice,
 	}
 }
 
diff --git a/internal/impl/codec_gen.go b/internal/impl/codec_gen.go
index fa41a85..fac941a 100644
--- a/internal/impl/codec_gen.go
+++ b/internal/impl/codec_gen.go
@@ -56,6 +56,7 @@
 	size:      sizeBool,
 	marshal:   appendBool,
 	unmarshal: consumeBool,
+	merge:     mergeBool,
 }
 
 // sizeBoolNoZero returns the size of wire encoding a bool pointer as a Bool.
@@ -84,6 +85,7 @@
 	size:      sizeBoolNoZero,
 	marshal:   appendBoolNoZero,
 	unmarshal: consumeBool,
+	merge:     mergeBoolNoZero,
 }
 
 // sizeBoolPtr returns the size of wire encoding a *bool pointer as a Bool.
@@ -134,6 +136,7 @@
 	size:      sizeBoolPtr,
 	marshal:   appendBoolPtr,
 	unmarshal: consumeBoolPtr,
+	merge:     mergeBoolPtr,
 }
 
 // sizeBoolSlice returns the size of wire encoding a []bool pointer as a repeated Bool.
@@ -212,6 +215,7 @@
 	size:      sizeBoolSlice,
 	marshal:   appendBoolSlice,
 	unmarshal: consumeBoolSlice,
+	merge:     mergeBoolSlice,
 }
 
 // sizeBoolPackedSlice returns the size of wire encoding a []bool pointer as a packed repeated Bool.
@@ -249,6 +253,7 @@
 	size:      sizeBoolPackedSlice,
 	marshal:   appendBoolPackedSlice,
 	unmarshal: consumeBoolSlice,
+	merge:     mergeBoolSlice,
 }
 
 // sizeBoolValue returns the size of wire encoding a bool value as a Bool.
@@ -290,6 +295,7 @@
 	size:      sizeBoolValue,
 	marshal:   appendBoolValue,
 	unmarshal: consumeBoolValue,
+	merge:     mergeScalarValue,
 }
 
 // sizeBoolSliceValue returns the size of wire encoding a []bool value as a repeated Bool.
@@ -368,6 +374,7 @@
 	size:      sizeBoolSliceValue,
 	marshal:   appendBoolSliceValue,
 	unmarshal: consumeBoolSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeBoolPackedSliceValue returns the size of wire encoding a []bool value as a packed repeated Bool.
@@ -410,6 +417,7 @@
 	size:      sizeBoolPackedSliceValue,
 	marshal:   appendBoolPackedSliceValue,
 	unmarshal: consumeBoolSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeEnumValue returns the size of wire encoding a  value as a Enum.
@@ -451,6 +459,7 @@
 	size:      sizeEnumValue,
 	marshal:   appendEnumValue,
 	unmarshal: consumeEnumValue,
+	merge:     mergeScalarValue,
 }
 
 // sizeEnumSliceValue returns the size of wire encoding a [] value as a repeated Enum.
@@ -529,6 +538,7 @@
 	size:      sizeEnumSliceValue,
 	marshal:   appendEnumSliceValue,
 	unmarshal: consumeEnumSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeEnumPackedSliceValue returns the size of wire encoding a [] value as a packed repeated Enum.
@@ -571,6 +581,7 @@
 	size:      sizeEnumPackedSliceValue,
 	marshal:   appendEnumPackedSliceValue,
 	unmarshal: consumeEnumSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeInt32 returns the size of wire encoding a int32 pointer as a Int32.
@@ -615,6 +626,7 @@
 	size:      sizeInt32,
 	marshal:   appendInt32,
 	unmarshal: consumeInt32,
+	merge:     mergeInt32,
 }
 
 // sizeInt32NoZero returns the size of wire encoding a int32 pointer as a Int32.
@@ -643,6 +655,7 @@
 	size:      sizeInt32NoZero,
 	marshal:   appendInt32NoZero,
 	unmarshal: consumeInt32,
+	merge:     mergeInt32NoZero,
 }
 
 // sizeInt32Ptr returns the size of wire encoding a *int32 pointer as a Int32.
@@ -693,6 +706,7 @@
 	size:      sizeInt32Ptr,
 	marshal:   appendInt32Ptr,
 	unmarshal: consumeInt32Ptr,
+	merge:     mergeInt32Ptr,
 }
 
 // sizeInt32Slice returns the size of wire encoding a []int32 pointer as a repeated Int32.
@@ -771,6 +785,7 @@
 	size:      sizeInt32Slice,
 	marshal:   appendInt32Slice,
 	unmarshal: consumeInt32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeInt32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Int32.
@@ -808,6 +823,7 @@
 	size:      sizeInt32PackedSlice,
 	marshal:   appendInt32PackedSlice,
 	unmarshal: consumeInt32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeInt32Value returns the size of wire encoding a int32 value as a Int32.
@@ -849,6 +865,7 @@
 	size:      sizeInt32Value,
 	marshal:   appendInt32Value,
 	unmarshal: consumeInt32Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeInt32SliceValue returns the size of wire encoding a []int32 value as a repeated Int32.
@@ -927,6 +944,7 @@
 	size:      sizeInt32SliceValue,
 	marshal:   appendInt32SliceValue,
 	unmarshal: consumeInt32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeInt32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Int32.
@@ -969,6 +987,7 @@
 	size:      sizeInt32PackedSliceValue,
 	marshal:   appendInt32PackedSliceValue,
 	unmarshal: consumeInt32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSint32 returns the size of wire encoding a int32 pointer as a Sint32.
@@ -1013,6 +1032,7 @@
 	size:      sizeSint32,
 	marshal:   appendSint32,
 	unmarshal: consumeSint32,
+	merge:     mergeInt32,
 }
 
 // sizeSint32NoZero returns the size of wire encoding a int32 pointer as a Sint32.
@@ -1041,6 +1061,7 @@
 	size:      sizeSint32NoZero,
 	marshal:   appendSint32NoZero,
 	unmarshal: consumeSint32,
+	merge:     mergeInt32NoZero,
 }
 
 // sizeSint32Ptr returns the size of wire encoding a *int32 pointer as a Sint32.
@@ -1091,6 +1112,7 @@
 	size:      sizeSint32Ptr,
 	marshal:   appendSint32Ptr,
 	unmarshal: consumeSint32Ptr,
+	merge:     mergeInt32Ptr,
 }
 
 // sizeSint32Slice returns the size of wire encoding a []int32 pointer as a repeated Sint32.
@@ -1169,6 +1191,7 @@
 	size:      sizeSint32Slice,
 	marshal:   appendSint32Slice,
 	unmarshal: consumeSint32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeSint32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Sint32.
@@ -1206,6 +1229,7 @@
 	size:      sizeSint32PackedSlice,
 	marshal:   appendSint32PackedSlice,
 	unmarshal: consumeSint32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeSint32Value returns the size of wire encoding a int32 value as a Sint32.
@@ -1247,6 +1271,7 @@
 	size:      sizeSint32Value,
 	marshal:   appendSint32Value,
 	unmarshal: consumeSint32Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeSint32SliceValue returns the size of wire encoding a []int32 value as a repeated Sint32.
@@ -1325,6 +1350,7 @@
 	size:      sizeSint32SliceValue,
 	marshal:   appendSint32SliceValue,
 	unmarshal: consumeSint32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSint32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Sint32.
@@ -1367,6 +1393,7 @@
 	size:      sizeSint32PackedSliceValue,
 	marshal:   appendSint32PackedSliceValue,
 	unmarshal: consumeSint32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeUint32 returns the size of wire encoding a uint32 pointer as a Uint32.
@@ -1411,6 +1438,7 @@
 	size:      sizeUint32,
 	marshal:   appendUint32,
 	unmarshal: consumeUint32,
+	merge:     mergeUint32,
 }
 
 // sizeUint32NoZero returns the size of wire encoding a uint32 pointer as a Uint32.
@@ -1439,6 +1467,7 @@
 	size:      sizeUint32NoZero,
 	marshal:   appendUint32NoZero,
 	unmarshal: consumeUint32,
+	merge:     mergeUint32NoZero,
 }
 
 // sizeUint32Ptr returns the size of wire encoding a *uint32 pointer as a Uint32.
@@ -1489,6 +1518,7 @@
 	size:      sizeUint32Ptr,
 	marshal:   appendUint32Ptr,
 	unmarshal: consumeUint32Ptr,
+	merge:     mergeUint32Ptr,
 }
 
 // sizeUint32Slice returns the size of wire encoding a []uint32 pointer as a repeated Uint32.
@@ -1567,6 +1597,7 @@
 	size:      sizeUint32Slice,
 	marshal:   appendUint32Slice,
 	unmarshal: consumeUint32Slice,
+	merge:     mergeUint32Slice,
 }
 
 // sizeUint32PackedSlice returns the size of wire encoding a []uint32 pointer as a packed repeated Uint32.
@@ -1604,6 +1635,7 @@
 	size:      sizeUint32PackedSlice,
 	marshal:   appendUint32PackedSlice,
 	unmarshal: consumeUint32Slice,
+	merge:     mergeUint32Slice,
 }
 
 // sizeUint32Value returns the size of wire encoding a uint32 value as a Uint32.
@@ -1645,6 +1677,7 @@
 	size:      sizeUint32Value,
 	marshal:   appendUint32Value,
 	unmarshal: consumeUint32Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeUint32SliceValue returns the size of wire encoding a []uint32 value as a repeated Uint32.
@@ -1723,6 +1756,7 @@
 	size:      sizeUint32SliceValue,
 	marshal:   appendUint32SliceValue,
 	unmarshal: consumeUint32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeUint32PackedSliceValue returns the size of wire encoding a []uint32 value as a packed repeated Uint32.
@@ -1765,6 +1799,7 @@
 	size:      sizeUint32PackedSliceValue,
 	marshal:   appendUint32PackedSliceValue,
 	unmarshal: consumeUint32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeInt64 returns the size of wire encoding a int64 pointer as a Int64.
@@ -1809,6 +1844,7 @@
 	size:      sizeInt64,
 	marshal:   appendInt64,
 	unmarshal: consumeInt64,
+	merge:     mergeInt64,
 }
 
 // sizeInt64NoZero returns the size of wire encoding a int64 pointer as a Int64.
@@ -1837,6 +1873,7 @@
 	size:      sizeInt64NoZero,
 	marshal:   appendInt64NoZero,
 	unmarshal: consumeInt64,
+	merge:     mergeInt64NoZero,
 }
 
 // sizeInt64Ptr returns the size of wire encoding a *int64 pointer as a Int64.
@@ -1887,6 +1924,7 @@
 	size:      sizeInt64Ptr,
 	marshal:   appendInt64Ptr,
 	unmarshal: consumeInt64Ptr,
+	merge:     mergeInt64Ptr,
 }
 
 // sizeInt64Slice returns the size of wire encoding a []int64 pointer as a repeated Int64.
@@ -1965,6 +2003,7 @@
 	size:      sizeInt64Slice,
 	marshal:   appendInt64Slice,
 	unmarshal: consumeInt64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeInt64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Int64.
@@ -2002,6 +2041,7 @@
 	size:      sizeInt64PackedSlice,
 	marshal:   appendInt64PackedSlice,
 	unmarshal: consumeInt64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeInt64Value returns the size of wire encoding a int64 value as a Int64.
@@ -2043,6 +2083,7 @@
 	size:      sizeInt64Value,
 	marshal:   appendInt64Value,
 	unmarshal: consumeInt64Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeInt64SliceValue returns the size of wire encoding a []int64 value as a repeated Int64.
@@ -2121,6 +2162,7 @@
 	size:      sizeInt64SliceValue,
 	marshal:   appendInt64SliceValue,
 	unmarshal: consumeInt64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeInt64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Int64.
@@ -2163,6 +2205,7 @@
 	size:      sizeInt64PackedSliceValue,
 	marshal:   appendInt64PackedSliceValue,
 	unmarshal: consumeInt64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSint64 returns the size of wire encoding a int64 pointer as a Sint64.
@@ -2207,6 +2250,7 @@
 	size:      sizeSint64,
 	marshal:   appendSint64,
 	unmarshal: consumeSint64,
+	merge:     mergeInt64,
 }
 
 // sizeSint64NoZero returns the size of wire encoding a int64 pointer as a Sint64.
@@ -2235,6 +2279,7 @@
 	size:      sizeSint64NoZero,
 	marshal:   appendSint64NoZero,
 	unmarshal: consumeSint64,
+	merge:     mergeInt64NoZero,
 }
 
 // sizeSint64Ptr returns the size of wire encoding a *int64 pointer as a Sint64.
@@ -2285,6 +2330,7 @@
 	size:      sizeSint64Ptr,
 	marshal:   appendSint64Ptr,
 	unmarshal: consumeSint64Ptr,
+	merge:     mergeInt64Ptr,
 }
 
 // sizeSint64Slice returns the size of wire encoding a []int64 pointer as a repeated Sint64.
@@ -2363,6 +2409,7 @@
 	size:      sizeSint64Slice,
 	marshal:   appendSint64Slice,
 	unmarshal: consumeSint64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeSint64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Sint64.
@@ -2400,6 +2447,7 @@
 	size:      sizeSint64PackedSlice,
 	marshal:   appendSint64PackedSlice,
 	unmarshal: consumeSint64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeSint64Value returns the size of wire encoding a int64 value as a Sint64.
@@ -2441,6 +2489,7 @@
 	size:      sizeSint64Value,
 	marshal:   appendSint64Value,
 	unmarshal: consumeSint64Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeSint64SliceValue returns the size of wire encoding a []int64 value as a repeated Sint64.
@@ -2519,6 +2568,7 @@
 	size:      sizeSint64SliceValue,
 	marshal:   appendSint64SliceValue,
 	unmarshal: consumeSint64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSint64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Sint64.
@@ -2561,6 +2611,7 @@
 	size:      sizeSint64PackedSliceValue,
 	marshal:   appendSint64PackedSliceValue,
 	unmarshal: consumeSint64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeUint64 returns the size of wire encoding a uint64 pointer as a Uint64.
@@ -2605,6 +2656,7 @@
 	size:      sizeUint64,
 	marshal:   appendUint64,
 	unmarshal: consumeUint64,
+	merge:     mergeUint64,
 }
 
 // sizeUint64NoZero returns the size of wire encoding a uint64 pointer as a Uint64.
@@ -2633,6 +2685,7 @@
 	size:      sizeUint64NoZero,
 	marshal:   appendUint64NoZero,
 	unmarshal: consumeUint64,
+	merge:     mergeUint64NoZero,
 }
 
 // sizeUint64Ptr returns the size of wire encoding a *uint64 pointer as a Uint64.
@@ -2683,6 +2736,7 @@
 	size:      sizeUint64Ptr,
 	marshal:   appendUint64Ptr,
 	unmarshal: consumeUint64Ptr,
+	merge:     mergeUint64Ptr,
 }
 
 // sizeUint64Slice returns the size of wire encoding a []uint64 pointer as a repeated Uint64.
@@ -2761,6 +2815,7 @@
 	size:      sizeUint64Slice,
 	marshal:   appendUint64Slice,
 	unmarshal: consumeUint64Slice,
+	merge:     mergeUint64Slice,
 }
 
 // sizeUint64PackedSlice returns the size of wire encoding a []uint64 pointer as a packed repeated Uint64.
@@ -2798,6 +2853,7 @@
 	size:      sizeUint64PackedSlice,
 	marshal:   appendUint64PackedSlice,
 	unmarshal: consumeUint64Slice,
+	merge:     mergeUint64Slice,
 }
 
 // sizeUint64Value returns the size of wire encoding a uint64 value as a Uint64.
@@ -2839,6 +2895,7 @@
 	size:      sizeUint64Value,
 	marshal:   appendUint64Value,
 	unmarshal: consumeUint64Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeUint64SliceValue returns the size of wire encoding a []uint64 value as a repeated Uint64.
@@ -2917,6 +2974,7 @@
 	size:      sizeUint64SliceValue,
 	marshal:   appendUint64SliceValue,
 	unmarshal: consumeUint64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeUint64PackedSliceValue returns the size of wire encoding a []uint64 value as a packed repeated Uint64.
@@ -2959,6 +3017,7 @@
 	size:      sizeUint64PackedSliceValue,
 	marshal:   appendUint64PackedSliceValue,
 	unmarshal: consumeUint64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSfixed32 returns the size of wire encoding a int32 pointer as a Sfixed32.
@@ -2993,6 +3052,7 @@
 	size:      sizeSfixed32,
 	marshal:   appendSfixed32,
 	unmarshal: consumeSfixed32,
+	merge:     mergeInt32,
 }
 
 // sizeSfixed32NoZero returns the size of wire encoding a int32 pointer as a Sfixed32.
@@ -3021,6 +3081,7 @@
 	size:      sizeSfixed32NoZero,
 	marshal:   appendSfixed32NoZero,
 	unmarshal: consumeSfixed32,
+	merge:     mergeInt32NoZero,
 }
 
 // sizeSfixed32Ptr returns the size of wire encoding a *int32 pointer as a Sfixed32.
@@ -3060,6 +3121,7 @@
 	size:      sizeSfixed32Ptr,
 	marshal:   appendSfixed32Ptr,
 	unmarshal: consumeSfixed32Ptr,
+	merge:     mergeInt32Ptr,
 }
 
 // sizeSfixed32Slice returns the size of wire encoding a []int32 pointer as a repeated Sfixed32.
@@ -3116,6 +3178,7 @@
 	size:      sizeSfixed32Slice,
 	marshal:   appendSfixed32Slice,
 	unmarshal: consumeSfixed32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeSfixed32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Sfixed32.
@@ -3147,6 +3210,7 @@
 	size:      sizeSfixed32PackedSlice,
 	marshal:   appendSfixed32PackedSlice,
 	unmarshal: consumeSfixed32Slice,
+	merge:     mergeInt32Slice,
 }
 
 // sizeSfixed32Value returns the size of wire encoding a int32 value as a Sfixed32.
@@ -3178,6 +3242,7 @@
 	size:      sizeSfixed32Value,
 	marshal:   appendSfixed32Value,
 	unmarshal: consumeSfixed32Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeSfixed32SliceValue returns the size of wire encoding a []int32 value as a repeated Sfixed32.
@@ -3233,6 +3298,7 @@
 	size:      sizeSfixed32SliceValue,
 	marshal:   appendSfixed32SliceValue,
 	unmarshal: consumeSfixed32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSfixed32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Sfixed32.
@@ -3267,6 +3333,7 @@
 	size:      sizeSfixed32PackedSliceValue,
 	marshal:   appendSfixed32PackedSliceValue,
 	unmarshal: consumeSfixed32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFixed32 returns the size of wire encoding a uint32 pointer as a Fixed32.
@@ -3301,6 +3368,7 @@
 	size:      sizeFixed32,
 	marshal:   appendFixed32,
 	unmarshal: consumeFixed32,
+	merge:     mergeUint32,
 }
 
 // sizeFixed32NoZero returns the size of wire encoding a uint32 pointer as a Fixed32.
@@ -3329,6 +3397,7 @@
 	size:      sizeFixed32NoZero,
 	marshal:   appendFixed32NoZero,
 	unmarshal: consumeFixed32,
+	merge:     mergeUint32NoZero,
 }
 
 // sizeFixed32Ptr returns the size of wire encoding a *uint32 pointer as a Fixed32.
@@ -3368,6 +3437,7 @@
 	size:      sizeFixed32Ptr,
 	marshal:   appendFixed32Ptr,
 	unmarshal: consumeFixed32Ptr,
+	merge:     mergeUint32Ptr,
 }
 
 // sizeFixed32Slice returns the size of wire encoding a []uint32 pointer as a repeated Fixed32.
@@ -3424,6 +3494,7 @@
 	size:      sizeFixed32Slice,
 	marshal:   appendFixed32Slice,
 	unmarshal: consumeFixed32Slice,
+	merge:     mergeUint32Slice,
 }
 
 // sizeFixed32PackedSlice returns the size of wire encoding a []uint32 pointer as a packed repeated Fixed32.
@@ -3455,6 +3526,7 @@
 	size:      sizeFixed32PackedSlice,
 	marshal:   appendFixed32PackedSlice,
 	unmarshal: consumeFixed32Slice,
+	merge:     mergeUint32Slice,
 }
 
 // sizeFixed32Value returns the size of wire encoding a uint32 value as a Fixed32.
@@ -3486,6 +3558,7 @@
 	size:      sizeFixed32Value,
 	marshal:   appendFixed32Value,
 	unmarshal: consumeFixed32Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeFixed32SliceValue returns the size of wire encoding a []uint32 value as a repeated Fixed32.
@@ -3541,6 +3614,7 @@
 	size:      sizeFixed32SliceValue,
 	marshal:   appendFixed32SliceValue,
 	unmarshal: consumeFixed32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFixed32PackedSliceValue returns the size of wire encoding a []uint32 value as a packed repeated Fixed32.
@@ -3575,6 +3649,7 @@
 	size:      sizeFixed32PackedSliceValue,
 	marshal:   appendFixed32PackedSliceValue,
 	unmarshal: consumeFixed32SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFloat returns the size of wire encoding a float32 pointer as a Float.
@@ -3609,6 +3684,7 @@
 	size:      sizeFloat,
 	marshal:   appendFloat,
 	unmarshal: consumeFloat,
+	merge:     mergeFloat32,
 }
 
 // sizeFloatNoZero returns the size of wire encoding a float32 pointer as a Float.
@@ -3637,6 +3713,7 @@
 	size:      sizeFloatNoZero,
 	marshal:   appendFloatNoZero,
 	unmarshal: consumeFloat,
+	merge:     mergeFloat32NoZero,
 }
 
 // sizeFloatPtr returns the size of wire encoding a *float32 pointer as a Float.
@@ -3676,6 +3753,7 @@
 	size:      sizeFloatPtr,
 	marshal:   appendFloatPtr,
 	unmarshal: consumeFloatPtr,
+	merge:     mergeFloat32Ptr,
 }
 
 // sizeFloatSlice returns the size of wire encoding a []float32 pointer as a repeated Float.
@@ -3732,6 +3810,7 @@
 	size:      sizeFloatSlice,
 	marshal:   appendFloatSlice,
 	unmarshal: consumeFloatSlice,
+	merge:     mergeFloat32Slice,
 }
 
 // sizeFloatPackedSlice returns the size of wire encoding a []float32 pointer as a packed repeated Float.
@@ -3763,6 +3842,7 @@
 	size:      sizeFloatPackedSlice,
 	marshal:   appendFloatPackedSlice,
 	unmarshal: consumeFloatSlice,
+	merge:     mergeFloat32Slice,
 }
 
 // sizeFloatValue returns the size of wire encoding a float32 value as a Float.
@@ -3794,6 +3874,7 @@
 	size:      sizeFloatValue,
 	marshal:   appendFloatValue,
 	unmarshal: consumeFloatValue,
+	merge:     mergeScalarValue,
 }
 
 // sizeFloatSliceValue returns the size of wire encoding a []float32 value as a repeated Float.
@@ -3849,6 +3930,7 @@
 	size:      sizeFloatSliceValue,
 	marshal:   appendFloatSliceValue,
 	unmarshal: consumeFloatSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFloatPackedSliceValue returns the size of wire encoding a []float32 value as a packed repeated Float.
@@ -3883,6 +3965,7 @@
 	size:      sizeFloatPackedSliceValue,
 	marshal:   appendFloatPackedSliceValue,
 	unmarshal: consumeFloatSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSfixed64 returns the size of wire encoding a int64 pointer as a Sfixed64.
@@ -3917,6 +4000,7 @@
 	size:      sizeSfixed64,
 	marshal:   appendSfixed64,
 	unmarshal: consumeSfixed64,
+	merge:     mergeInt64,
 }
 
 // sizeSfixed64NoZero returns the size of wire encoding a int64 pointer as a Sfixed64.
@@ -3945,6 +4029,7 @@
 	size:      sizeSfixed64NoZero,
 	marshal:   appendSfixed64NoZero,
 	unmarshal: consumeSfixed64,
+	merge:     mergeInt64NoZero,
 }
 
 // sizeSfixed64Ptr returns the size of wire encoding a *int64 pointer as a Sfixed64.
@@ -3984,6 +4069,7 @@
 	size:      sizeSfixed64Ptr,
 	marshal:   appendSfixed64Ptr,
 	unmarshal: consumeSfixed64Ptr,
+	merge:     mergeInt64Ptr,
 }
 
 // sizeSfixed64Slice returns the size of wire encoding a []int64 pointer as a repeated Sfixed64.
@@ -4040,6 +4126,7 @@
 	size:      sizeSfixed64Slice,
 	marshal:   appendSfixed64Slice,
 	unmarshal: consumeSfixed64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeSfixed64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Sfixed64.
@@ -4071,6 +4158,7 @@
 	size:      sizeSfixed64PackedSlice,
 	marshal:   appendSfixed64PackedSlice,
 	unmarshal: consumeSfixed64Slice,
+	merge:     mergeInt64Slice,
 }
 
 // sizeSfixed64Value returns the size of wire encoding a int64 value as a Sfixed64.
@@ -4102,6 +4190,7 @@
 	size:      sizeSfixed64Value,
 	marshal:   appendSfixed64Value,
 	unmarshal: consumeSfixed64Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeSfixed64SliceValue returns the size of wire encoding a []int64 value as a repeated Sfixed64.
@@ -4157,6 +4246,7 @@
 	size:      sizeSfixed64SliceValue,
 	marshal:   appendSfixed64SliceValue,
 	unmarshal: consumeSfixed64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeSfixed64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Sfixed64.
@@ -4191,6 +4281,7 @@
 	size:      sizeSfixed64PackedSliceValue,
 	marshal:   appendSfixed64PackedSliceValue,
 	unmarshal: consumeSfixed64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFixed64 returns the size of wire encoding a uint64 pointer as a Fixed64.
@@ -4225,6 +4316,7 @@
 	size:      sizeFixed64,
 	marshal:   appendFixed64,
 	unmarshal: consumeFixed64,
+	merge:     mergeUint64,
 }
 
 // sizeFixed64NoZero returns the size of wire encoding a uint64 pointer as a Fixed64.
@@ -4253,6 +4345,7 @@
 	size:      sizeFixed64NoZero,
 	marshal:   appendFixed64NoZero,
 	unmarshal: consumeFixed64,
+	merge:     mergeUint64NoZero,
 }
 
 // sizeFixed64Ptr returns the size of wire encoding a *uint64 pointer as a Fixed64.
@@ -4292,6 +4385,7 @@
 	size:      sizeFixed64Ptr,
 	marshal:   appendFixed64Ptr,
 	unmarshal: consumeFixed64Ptr,
+	merge:     mergeUint64Ptr,
 }
 
 // sizeFixed64Slice returns the size of wire encoding a []uint64 pointer as a repeated Fixed64.
@@ -4348,6 +4442,7 @@
 	size:      sizeFixed64Slice,
 	marshal:   appendFixed64Slice,
 	unmarshal: consumeFixed64Slice,
+	merge:     mergeUint64Slice,
 }
 
 // sizeFixed64PackedSlice returns the size of wire encoding a []uint64 pointer as a packed repeated Fixed64.
@@ -4379,6 +4474,7 @@
 	size:      sizeFixed64PackedSlice,
 	marshal:   appendFixed64PackedSlice,
 	unmarshal: consumeFixed64Slice,
+	merge:     mergeUint64Slice,
 }
 
 // sizeFixed64Value returns the size of wire encoding a uint64 value as a Fixed64.
@@ -4410,6 +4506,7 @@
 	size:      sizeFixed64Value,
 	marshal:   appendFixed64Value,
 	unmarshal: consumeFixed64Value,
+	merge:     mergeScalarValue,
 }
 
 // sizeFixed64SliceValue returns the size of wire encoding a []uint64 value as a repeated Fixed64.
@@ -4465,6 +4562,7 @@
 	size:      sizeFixed64SliceValue,
 	marshal:   appendFixed64SliceValue,
 	unmarshal: consumeFixed64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeFixed64PackedSliceValue returns the size of wire encoding a []uint64 value as a packed repeated Fixed64.
@@ -4499,6 +4597,7 @@
 	size:      sizeFixed64PackedSliceValue,
 	marshal:   appendFixed64PackedSliceValue,
 	unmarshal: consumeFixed64SliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeDouble returns the size of wire encoding a float64 pointer as a Double.
@@ -4533,6 +4632,7 @@
 	size:      sizeDouble,
 	marshal:   appendDouble,
 	unmarshal: consumeDouble,
+	merge:     mergeFloat64,
 }
 
 // sizeDoubleNoZero returns the size of wire encoding a float64 pointer as a Double.
@@ -4561,6 +4661,7 @@
 	size:      sizeDoubleNoZero,
 	marshal:   appendDoubleNoZero,
 	unmarshal: consumeDouble,
+	merge:     mergeFloat64NoZero,
 }
 
 // sizeDoublePtr returns the size of wire encoding a *float64 pointer as a Double.
@@ -4600,6 +4701,7 @@
 	size:      sizeDoublePtr,
 	marshal:   appendDoublePtr,
 	unmarshal: consumeDoublePtr,
+	merge:     mergeFloat64Ptr,
 }
 
 // sizeDoubleSlice returns the size of wire encoding a []float64 pointer as a repeated Double.
@@ -4656,6 +4758,7 @@
 	size:      sizeDoubleSlice,
 	marshal:   appendDoubleSlice,
 	unmarshal: consumeDoubleSlice,
+	merge:     mergeFloat64Slice,
 }
 
 // sizeDoublePackedSlice returns the size of wire encoding a []float64 pointer as a packed repeated Double.
@@ -4687,6 +4790,7 @@
 	size:      sizeDoublePackedSlice,
 	marshal:   appendDoublePackedSlice,
 	unmarshal: consumeDoubleSlice,
+	merge:     mergeFloat64Slice,
 }
 
 // sizeDoubleValue returns the size of wire encoding a float64 value as a Double.
@@ -4718,6 +4822,7 @@
 	size:      sizeDoubleValue,
 	marshal:   appendDoubleValue,
 	unmarshal: consumeDoubleValue,
+	merge:     mergeScalarValue,
 }
 
 // sizeDoubleSliceValue returns the size of wire encoding a []float64 value as a repeated Double.
@@ -4773,6 +4878,7 @@
 	size:      sizeDoubleSliceValue,
 	marshal:   appendDoubleSliceValue,
 	unmarshal: consumeDoubleSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeDoublePackedSliceValue returns the size of wire encoding a []float64 value as a packed repeated Double.
@@ -4807,6 +4913,7 @@
 	size:      sizeDoublePackedSliceValue,
 	marshal:   appendDoublePackedSliceValue,
 	unmarshal: consumeDoubleSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeString returns the size of wire encoding a string pointer as a String.
@@ -4841,6 +4948,7 @@
 	size:      sizeString,
 	marshal:   appendString,
 	unmarshal: consumeString,
+	merge:     mergeString,
 }
 
 // appendStringValidateUTF8 wire encodes a string pointer as a String.
@@ -4875,6 +4983,7 @@
 	size:      sizeString,
 	marshal:   appendStringValidateUTF8,
 	unmarshal: consumeStringValidateUTF8,
+	merge:     mergeString,
 }
 
 // sizeStringNoZero returns the size of wire encoding a string pointer as a String.
@@ -4903,6 +5012,7 @@
 	size:      sizeStringNoZero,
 	marshal:   appendStringNoZero,
 	unmarshal: consumeString,
+	merge:     mergeStringNoZero,
 }
 
 // appendStringNoZeroValidateUTF8 wire encodes a string pointer as a String.
@@ -4924,6 +5034,7 @@
 	size:      sizeStringNoZero,
 	marshal:   appendStringNoZeroValidateUTF8,
 	unmarshal: consumeStringValidateUTF8,
+	merge:     mergeStringNoZero,
 }
 
 // sizeStringPtr returns the size of wire encoding a *string pointer as a String.
@@ -4964,6 +5075,7 @@
 	size:      sizeStringPtr,
 	marshal:   appendStringPtr,
 	unmarshal: consumeStringPtr,
+	merge:     mergeStringPtr,
 }
 
 // sizeStringSlice returns the size of wire encoding a []string pointer as a repeated String.
@@ -5004,6 +5116,7 @@
 	size:      sizeStringSlice,
 	marshal:   appendStringSlice,
 	unmarshal: consumeStringSlice,
+	merge:     mergeStringSlice,
 }
 
 // appendStringSliceValidateUTF8 encodes a []string pointer as a repeated String.
@@ -5041,6 +5154,7 @@
 	size:      sizeStringSlice,
 	marshal:   appendStringSliceValidateUTF8,
 	unmarshal: consumeStringSliceValidateUTF8,
+	merge:     mergeStringSlice,
 }
 
 // sizeStringValue returns the size of wire encoding a string value as a String.
@@ -5072,6 +5186,7 @@
 	size:      sizeStringValue,
 	marshal:   appendStringValue,
 	unmarshal: consumeStringValue,
+	merge:     mergeScalarValue,
 }
 
 // appendStringValueValidateUTF8 encodes a string value as a String.
@@ -5104,6 +5219,7 @@
 	size:      sizeStringValue,
 	marshal:   appendStringValueValidateUTF8,
 	unmarshal: consumeStringValueValidateUTF8,
+	merge:     mergeScalarValue,
 }
 
 // sizeStringSliceValue returns the size of wire encoding a []string value as a repeated String.
@@ -5146,6 +5262,7 @@
 	size:      sizeStringSliceValue,
 	marshal:   appendStringSliceValue,
 	unmarshal: consumeStringSliceValue,
+	merge:     mergeListValue,
 }
 
 // sizeBytes returns the size of wire encoding a []byte pointer as a Bytes.
@@ -5180,6 +5297,7 @@
 	size:      sizeBytes,
 	marshal:   appendBytes,
 	unmarshal: consumeBytes,
+	merge:     mergeBytes,
 }
 
 // appendBytesValidateUTF8 wire encodes a []byte pointer as a Bytes.
@@ -5214,6 +5332,7 @@
 	size:      sizeBytes,
 	marshal:   appendBytesValidateUTF8,
 	unmarshal: consumeBytesValidateUTF8,
+	merge:     mergeBytes,
 }
 
 // sizeBytesNoZero returns the size of wire encoding a []byte pointer as a Bytes.
@@ -5257,6 +5376,7 @@
 	size:      sizeBytesNoZero,
 	marshal:   appendBytesNoZero,
 	unmarshal: consumeBytesNoZero,
+	merge:     mergeBytesNoZero,
 }
 
 // appendBytesNoZeroValidateUTF8 wire encodes a []byte pointer as a Bytes.
@@ -5295,6 +5415,7 @@
 	size:      sizeBytesNoZero,
 	marshal:   appendBytesNoZeroValidateUTF8,
 	unmarshal: consumeBytesNoZeroValidateUTF8,
+	merge:     mergeBytesNoZero,
 }
 
 // sizeBytesSlice returns the size of wire encoding a [][]byte pointer as a repeated Bytes.
@@ -5335,6 +5456,7 @@
 	size:      sizeBytesSlice,
 	marshal:   appendBytesSlice,
 	unmarshal: consumeBytesSlice,
+	merge:     mergeBytesSlice,
 }
 
 // appendBytesSliceValidateUTF8 encodes a [][]byte pointer as a repeated Bytes.
@@ -5372,6 +5494,7 @@
 	size:      sizeBytesSlice,
 	marshal:   appendBytesSliceValidateUTF8,
 	unmarshal: consumeBytesSliceValidateUTF8,
+	merge:     mergeBytesSlice,
 }
 
 // sizeBytesValue returns the size of wire encoding a []byte value as a Bytes.
@@ -5403,6 +5526,7 @@
 	size:      sizeBytesValue,
 	marshal:   appendBytesValue,
 	unmarshal: consumeBytesValue,
+	merge:     mergeBytesValue,
 }
 
 // sizeBytesSliceValue returns the size of wire encoding a [][]byte value as a repeated Bytes.
@@ -5445,6 +5569,7 @@
 	size:      sizeBytesSliceValue,
 	marshal:   appendBytesSliceValue,
 	unmarshal: consumeBytesSliceValue,
+	merge:     mergeBytesListValue,
 }
 
 // We append to an empty array rather than a nil []byte to get non-nil zero-length byte slices.
diff --git a/internal/impl/codec_map.go b/internal/impl/codec_map.go
index b319583..dfeb944 100644
--- a/internal/impl/codec_map.go
+++ b/internal/impl/codec_map.go
@@ -67,6 +67,14 @@
 			}
 		},
 	}
+	switch valField.Kind() {
+	case pref.MessageKind:
+		funcs.merge = mergeMapOfMessage
+	case pref.BytesKind:
+		funcs.merge = mergeMapOfBytes
+	default:
+		funcs.merge = mergeMap
+	}
 	if valFuncs.isInit != nil {
 		funcs.isInit = func(p pointer, f *coderFieldInfo) error {
 			return isInitMap(p.AsValueOf(ft).Elem(), mapi, f)
@@ -327,3 +335,54 @@
 	}
 	return nil
 }
+
+func mergeMap(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+	dstm := dst.AsValueOf(f.ft).Elem()
+	srcm := src.AsValueOf(f.ft).Elem()
+	if srcm.Len() == 0 {
+		return
+	}
+	if dstm.IsNil() {
+		dstm.Set(reflect.MakeMap(f.ft))
+	}
+	iter := mapRange(srcm)
+	for iter.Next() {
+		dstm.SetMapIndex(iter.Key(), iter.Value())
+	}
+}
+
+func mergeMapOfBytes(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+	dstm := dst.AsValueOf(f.ft).Elem()
+	srcm := src.AsValueOf(f.ft).Elem()
+	if srcm.Len() == 0 {
+		return
+	}
+	if dstm.IsNil() {
+		dstm.Set(reflect.MakeMap(f.ft))
+	}
+	iter := mapRange(srcm)
+	for iter.Next() {
+		dstm.SetMapIndex(iter.Key(), reflect.ValueOf(append(emptyBuf[:], iter.Value().Bytes()...)))
+	}
+}
+
+func mergeMapOfMessage(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+	dstm := dst.AsValueOf(f.ft).Elem()
+	srcm := src.AsValueOf(f.ft).Elem()
+	if srcm.Len() == 0 {
+		return
+	}
+	if dstm.IsNil() {
+		dstm.Set(reflect.MakeMap(f.ft))
+	}
+	iter := mapRange(srcm)
+	for iter.Next() {
+		val := reflect.New(f.ft.Elem().Elem())
+		if f.mi != nil {
+			f.mi.mergePointer(pointerOfValue(val), pointerOfValue(iter.Value()), opts)
+		} else {
+			opts.Merge(asMessage(val), asMessage(iter.Value()))
+		}
+		dstm.SetMapIndex(iter.Key(), val)
+	}
+}
diff --git a/internal/impl/codec_message.go b/internal/impl/codec_message.go
index cd4ebe6..08f4d5a 100644
--- a/internal/impl/codec_message.go
+++ b/internal/impl/codec_message.go
@@ -151,4 +151,7 @@
 	if mi.methods.IsInitialized == nil {
 		mi.methods.IsInitialized = mi.isInitialized
 	}
+	if mi.methods.Merge == nil {
+		mi.methods.Merge = mi.merge
+	}
 }
diff --git a/internal/impl/codec_reflect.go b/internal/impl/codec_reflect.go
index ae872aa..85811b0 100644
--- a/internal/impl/codec_reflect.go
+++ b/internal/impl/codec_reflect.go
@@ -37,10 +37,15 @@
 	return out, nil
 }
 
+func mergeEnum(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	dst.v.Elem().Set(src.v.Elem())
+}
+
 var coderEnum = pointerCoderFuncs{
 	size:      sizeEnum,
 	marshal:   appendEnum,
 	unmarshal: consumeEnum,
+	merge:     mergeEnum,
 }
 
 func sizeEnumNoZero(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
@@ -57,10 +62,17 @@
 	return appendEnum(b, p, f, opts)
 }
 
+func mergeEnumNoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	if src.v.Elem().Int() != 0 {
+		dst.v.Elem().Set(src.v.Elem())
+	}
+}
+
 var coderEnumNoZero = pointerCoderFuncs{
 	size:      sizeEnumNoZero,
 	marshal:   appendEnumNoZero,
 	unmarshal: consumeEnum,
+	merge:     mergeEnumNoZero,
 }
 
 func sizeEnumPtr(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
@@ -81,10 +93,19 @@
 	return consumeEnum(b, pointer{p.v.Elem()}, wtyp, f, opts)
 }
 
+func mergeEnumPtr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	if !src.v.Elem().IsNil() {
+		v := reflect.New(dst.v.Type().Elem().Elem())
+		v.Elem().Set(src.v.Elem().Elem())
+		dst.v.Elem().Set(v)
+	}
+}
+
 var coderEnumPtr = pointerCoderFuncs{
 	size:      sizeEnumPtr,
 	marshal:   appendEnumPtr,
 	unmarshal: consumeEnumPtr,
+	merge:     mergeEnumPtr,
 }
 
 func sizeEnumSlice(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
@@ -138,10 +159,15 @@
 	return out, nil
 }
 
+func mergeEnumSlice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	dst.v.Elem().Set(reflect.AppendSlice(dst.v.Elem(), src.v.Elem()))
+}
+
 var coderEnumSlice = pointerCoderFuncs{
 	size:      sizeEnumSlice,
 	marshal:   appendEnumSlice,
 	unmarshal: consumeEnumSlice,
+	merge:     mergeEnumSlice,
 }
 
 func sizeEnumPackedSlice(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
@@ -179,4 +205,5 @@
 	size:      sizeEnumPackedSlice,
 	marshal:   appendEnumPackedSlice,
 	unmarshal: consumeEnumSlice,
+	merge:     mergeEnumSlice,
 }
diff --git a/internal/impl/codec_tables.go b/internal/impl/codec_tables.go
index 2c62bda..ef30356 100644
--- a/internal/impl/codec_tables.go
+++ b/internal/impl/codec_tables.go
@@ -20,6 +20,7 @@
 	marshal   func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error)
 	unmarshal func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error)
 	isInit    func(p pointer, f *coderFieldInfo) error
+	merge     func(dst, src pointer, f *coderFieldInfo, opts mergeOptions)
 }
 
 // valueCoderFuncs is a set of protoreflect.Value encoding functions.
@@ -28,6 +29,7 @@
 	marshal   func(b []byte, v pref.Value, wiretag uint64, opts marshalOptions) ([]byte, error)
 	unmarshal func(b []byte, v pref.Value, num wire.Number, wtyp wire.Type, opts unmarshalOptions) (pref.Value, unmarshalOutput, error)
 	isInit    func(v pref.Value) error
+	merge     func(dst, src pref.Value, opts mergeOptions) pref.Value
 }
 
 // fieldCoder returns pointer functions for a field, used for operating on
diff --git a/internal/impl/merge.go b/internal/impl/merge.go
new file mode 100644
index 0000000..20d9dfd
--- /dev/null
+++ b/internal/impl/merge.go
@@ -0,0 +1,176 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package impl
+
+import (
+	"fmt"
+	"reflect"
+
+	"google.golang.org/protobuf/proto"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	piface "google.golang.org/protobuf/runtime/protoiface"
+)
+
+type mergeOptions piface.MergeOptions
+
+func (o mergeOptions) Merge(dst, src proto.Message) {
+	proto.Merge(dst, src)
+}
+
+// merge is protoreflect.Methods.Merge.
+func (mi *MessageInfo) merge(dst, src pref.Message, in piface.MergeInput, opts piface.MergeOptions) piface.MergeOutput {
+	dp, ok := mi.getPointer(dst)
+	if !ok {
+		return piface.MergeOutput{Merged: false}
+	}
+	sp, ok := mi.getPointer(src)
+	if !ok {
+		return piface.MergeOutput{Merged: false}
+	}
+	mi.mergePointer(dp, sp, opts)
+	return piface.MergeOutput{Merged: true}
+}
+
+func (mi *MessageInfo) mergePointer(dst, src pointer, opts mergeOptions) {
+	mi.init()
+	if dst.IsNil() {
+		panic(fmt.Sprintf("invalid value: merging into nil message"))
+	}
+	if src.IsNil() {
+		return
+	}
+	for _, f := range mi.orderedCoderFields {
+		if f.funcs.merge == nil {
+			continue
+		}
+		sfptr := src.Apply(f.offset)
+		if f.isPointer && sfptr.Elem().IsNil() {
+			continue
+		}
+		f.funcs.merge(dst.Apply(f.offset), sfptr, f, opts)
+	}
+	if mi.extensionOffset.IsValid() {
+		sext := src.Apply(mi.extensionOffset).Extensions()
+		dext := dst.Apply(mi.extensionOffset).Extensions()
+		if *dext == nil {
+			*dext = make(map[int32]ExtensionField)
+		}
+		for num, sx := range *sext {
+			xt := sx.Type()
+			xi := getExtensionFieldInfo(xt)
+			if xi.funcs.merge == nil {
+				continue
+			}
+			dx := (*dext)[num]
+			var dv pref.Value
+			if dx.Type() == sx.Type() {
+				dv = dx.Value()
+			}
+			if !dv.IsValid() && xi.unmarshalNeedsValue {
+				dv = xt.New()
+			}
+			dv = xi.funcs.merge(dv, sx.Value(), opts)
+			dx.Set(sx.Type(), dv)
+			(*dext)[num] = dx
+		}
+	}
+	if mi.unknownOffset.IsValid() {
+		du := dst.Apply(mi.unknownOffset).Bytes()
+		su := src.Apply(mi.unknownOffset).Bytes()
+		if len(*su) > 0 {
+			*du = append(*du, *su...)
+		}
+	}
+}
+
+func mergeScalarValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	return src
+}
+
+func mergeBytesValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	return pref.ValueOfBytes(append(emptyBuf[:], src.Bytes()...))
+}
+
+func mergeListValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	dstl := dst.List()
+	srcl := src.List()
+	for i, llen := 0, srcl.Len(); i < llen; i++ {
+		dstl.Append(srcl.Get(i))
+	}
+	return dst
+}
+
+func mergeBytesListValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	dstl := dst.List()
+	srcl := src.List()
+	for i, llen := 0, srcl.Len(); i < llen; i++ {
+		sb := srcl.Get(i).Bytes()
+		db := append(emptyBuf[:], sb...)
+		dstl.Append(pref.ValueOfBytes(db))
+	}
+	return dst
+}
+
+func mergeMessageListValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	dstl := dst.List()
+	srcl := src.List()
+	for i, llen := 0, srcl.Len(); i < llen; i++ {
+		sm := srcl.Get(i).Message()
+		dm := proto.Clone(sm.Interface()).ProtoReflect()
+		dstl.Append(pref.ValueOfMessage(dm))
+	}
+	return dst
+}
+
+func mergeMessageValue(dst, src pref.Value, opts mergeOptions) pref.Value {
+	opts.Merge(dst.Message().Interface(), src.Message().Interface())
+	return dst
+}
+
+func mergeMessage(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+	if f.mi != nil {
+		if dst.Elem().IsNil() {
+			dst.SetPointer(pointerOfValue(reflect.New(f.mi.GoReflectType.Elem())))
+		}
+		f.mi.mergePointer(dst.Elem(), src.Elem(), opts)
+	} else {
+		dm := dst.AsValueOf(f.ft).Elem()
+		sm := src.AsValueOf(f.ft).Elem()
+		if dm.IsNil() {
+			dm.Set(reflect.New(f.ft.Elem()))
+		}
+		opts.Merge(asMessage(dm), asMessage(sm))
+	}
+}
+
+func mergeMessageSlice(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+	for _, sp := range src.PointerSlice() {
+		dm := reflect.New(f.ft.Elem().Elem())
+		if f.mi != nil {
+			f.mi.mergePointer(pointerOfValue(dm), sp, opts)
+		} else {
+			opts.Merge(asMessage(dm), asMessage(sp.AsValueOf(f.ft.Elem().Elem())))
+		}
+		dst.AppendPointerSlice(pointerOfValue(dm))
+	}
+}
+
+func mergeBytes(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Bytes() = append(emptyBuf[:], *src.Bytes()...)
+}
+
+func mergeBytesNoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Bytes()
+	if len(v) > 0 {
+		*dst.Bytes() = append(emptyBuf[:], v...)
+	}
+}
+
+func mergeBytesSlice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.BytesSlice()
+	for _, v := range *src.BytesSlice() {
+		*ds = append(*ds, append(emptyBuf[:], v...))
+	}
+}
diff --git a/internal/impl/merge_gen.go b/internal/impl/merge_gen.go
new file mode 100644
index 0000000..90a1323
--- /dev/null
+++ b/internal/impl/merge_gen.go
@@ -0,0 +1,209 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style.
+// license that can be found in the LICENSE file.
+
+// Code generated by generate-types. DO NOT EDIT.
+
+package impl
+
+import ()
+
+func mergeBool(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Bool() = *src.Bool()
+}
+
+func mergeBoolNoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Bool()
+	if v != false {
+		*dst.Bool() = v
+	}
+}
+
+func mergeBoolPtr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.BoolPtr()
+	if p != nil {
+		v := *p
+		*dst.BoolPtr() = &v
+	}
+}
+
+func mergeBoolSlice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.BoolSlice()
+	ss := src.BoolSlice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeInt32(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Int32() = *src.Int32()
+}
+
+func mergeInt32NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Int32()
+	if v != 0 {
+		*dst.Int32() = v
+	}
+}
+
+func mergeInt32Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Int32Ptr()
+	if p != nil {
+		v := *p
+		*dst.Int32Ptr() = &v
+	}
+}
+
+func mergeInt32Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Int32Slice()
+	ss := src.Int32Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeUint32(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Uint32() = *src.Uint32()
+}
+
+func mergeUint32NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Uint32()
+	if v != 0 {
+		*dst.Uint32() = v
+	}
+}
+
+func mergeUint32Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Uint32Ptr()
+	if p != nil {
+		v := *p
+		*dst.Uint32Ptr() = &v
+	}
+}
+
+func mergeUint32Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Uint32Slice()
+	ss := src.Uint32Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeInt64(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Int64() = *src.Int64()
+}
+
+func mergeInt64NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Int64()
+	if v != 0 {
+		*dst.Int64() = v
+	}
+}
+
+func mergeInt64Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Int64Ptr()
+	if p != nil {
+		v := *p
+		*dst.Int64Ptr() = &v
+	}
+}
+
+func mergeInt64Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Int64Slice()
+	ss := src.Int64Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeUint64(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Uint64() = *src.Uint64()
+}
+
+func mergeUint64NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Uint64()
+	if v != 0 {
+		*dst.Uint64() = v
+	}
+}
+
+func mergeUint64Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Uint64Ptr()
+	if p != nil {
+		v := *p
+		*dst.Uint64Ptr() = &v
+	}
+}
+
+func mergeUint64Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Uint64Slice()
+	ss := src.Uint64Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeFloat32(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Float32() = *src.Float32()
+}
+
+func mergeFloat32NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Float32()
+	if v != 0 {
+		*dst.Float32() = v
+	}
+}
+
+func mergeFloat32Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Float32Ptr()
+	if p != nil {
+		v := *p
+		*dst.Float32Ptr() = &v
+	}
+}
+
+func mergeFloat32Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Float32Slice()
+	ss := src.Float32Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeFloat64(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.Float64() = *src.Float64()
+}
+
+func mergeFloat64NoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.Float64()
+	if v != 0 {
+		*dst.Float64() = v
+	}
+}
+
+func mergeFloat64Ptr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.Float64Ptr()
+	if p != nil {
+		v := *p
+		*dst.Float64Ptr() = &v
+	}
+}
+
+func mergeFloat64Slice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.Float64Slice()
+	ss := src.Float64Slice()
+	*ds = append(*ds, *ss...)
+}
+
+func mergeString(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	*dst.String() = *src.String()
+}
+
+func mergeStringNoZero(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	v := *src.String()
+	if v != "" {
+		*dst.String() = v
+	}
+}
+
+func mergeStringPtr(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	p := *src.StringPtr()
+	if p != nil {
+		v := *p
+		*dst.StringPtr() = &v
+	}
+}
+
+func mergeStringSlice(dst, src pointer, _ *coderFieldInfo, _ mergeOptions) {
+	ds := dst.StringSlice()
+	ss := src.StringSlice()
+	*ds = append(*ds, *ss...)
+}
diff --git a/internal/impl/message.go b/internal/impl/message.go
index e464bef..4d18b96 100644
--- a/internal/impl/message.go
+++ b/internal/impl/message.go
@@ -94,6 +94,19 @@
 	atomic.StoreUint32(&mi.initDone, 1)
 }
 
+// getPointer returns the pointer for a message, which should be of
+// the type of the MessageInfo. If the message is of a different type,
+// it returns ok==false.
+func (mi *MessageInfo) getPointer(m pref.Message) (p pointer, ok bool) {
+	switch m := m.(type) {
+	case *messageState:
+		return m.pointer(), m.mi == mi
+	case *messageReflectWrapper:
+		return m.pointer(), m.mi == mi
+	}
+	return pointer{}, false
+}
+
 type (
 	SizeCache       = int32
 	WeakFields      = map[int32]piface.MessageV1
diff --git a/proto/merge.go b/proto/merge.go
index aecc8e4..701e4c0 100644
--- a/proto/merge.go
+++ b/proto/merge.go
@@ -6,6 +6,7 @@
 
 import (
 	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/runtime/protoiface"
 )
 
 // Merge merges src into dst, which must be a message with the same descriptor.
@@ -52,6 +53,16 @@
 type mergeOptions struct{}
 
 func (o mergeOptions) mergeMessage(dst, src protoreflect.Message) {
+	methods := protoMethods(dst)
+	if methods != nil && methods.Merge != nil {
+		var in protoiface.MergeInput
+		var opts protoiface.MergeOptions
+		out := methods.Merge(dst, src, in, opts)
+		if out.Merged {
+			return
+		}
+	}
+
 	src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
 		switch {
 		case fd.IsList():
diff --git a/reflect/protoreflect/methods.go b/reflect/protoreflect/methods.go
index 5a29d2c..9778b88 100644
--- a/reflect/protoreflect/methods.go
+++ b/reflect/protoreflect/methods.go
@@ -22,6 +22,7 @@
 		Marshal       func(Message, marshalInput, marshalOptions) (marshalOutput, error)
 		Unmarshal     func(Message, unmarshalInput, unmarshalOptions) (unmarshalOutput, error)
 		IsInitialized func(Message) error
+		Merge         func(Message, Message, mergeInput, mergeOptions) mergeOutput
 	}
 	supportFlags = uint64
 	marshalInput = struct {
@@ -52,4 +53,14 @@
 			FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error)
 		}
 	}
+	mergeInput = struct {
+		pragma.NoUnkeyedLiterals
+	}
+	mergeOutput = struct {
+		pragma.NoUnkeyedLiterals
+		Merged bool
+	}
+	mergeOptions = struct {
+		pragma.NoUnkeyedLiterals
+	}
 )
diff --git a/runtime/protoiface/methods.go b/runtime/protoiface/methods.go
index 54e7fb3..df2f252 100644
--- a/runtime/protoiface/methods.go
+++ b/runtime/protoiface/methods.go
@@ -27,15 +27,18 @@
 
 	// Marshal writes the wire-format encoding of m to the provided buffer.
 	// Size should be provided if a custom MarshalAppend is provided.
-	// It should not return an error for a partial message.
+	// It must not return an error for a partial message.
 	Marshal func(m protoreflect.Message, in MarshalInput, opts MarshalOptions) (MarshalOutput, error)
 
 	// Unmarshal parses the wire-format encoding of a message and merges the result to m.
-	// It should not reset the target message or return an error for a partial message.
+	// It must not reset the target message or return an error for a partial message.
 	Unmarshal func(m protoreflect.Message, in UnmarshalInput, opts UnmarshalOptions) (UnmarshalOutput, error)
 
 	// IsInitialized returns an error if any required fields in m are not set.
 	IsInitialized func(m protoreflect.Message) error
+
+	// Merge merges src into dst.
+	Merge func(dst, src protoreflect.Message, in MergeInput, opts MergeOptions) MergeOutput
 }
 
 type SupportFlags = uint64
@@ -113,3 +116,22 @@
 const (
 	UnmarshalDiscardUnknown UnmarshalFlags = 1 << iota
 )
+
+// MergeInput is input to the merger.
+type MergeInput = struct {
+	pragma.NoUnkeyedLiterals
+}
+
+// MergeOutput is output from the merger.
+type MergeOutput = struct {
+	pragma.NoUnkeyedLiterals
+
+	// Merged is true if the merge was performed, false otherwise.
+	// If false, the merger must have made no changes to the destination.
+	Merged bool
+}
+
+// MergeOptions configure the merger.
+type MergeOptions = struct {
+	pragma.NoUnkeyedLiterals
+}