Skip to content

Commit cc853ae

Browse files
authored
Introduce CompileMode compatibility API (pytorch#21721)
Differential Revision: D115453091 Pull Request resolved: pytorch#21721
1 parent b9cad48 commit cc853ae

8 files changed

Lines changed: 215 additions & 32 deletions

File tree

‎backends/cadence/aot/compiler.py‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@
4545
from torch.export.exported_program import ExportedProgram
4646
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e
4747

48-
from .pass_utils import EdgePassesConfig
48+
from .pass_utils import (
49+
CompileMode,
50+
EdgePassesConfig,
51+
normalize_compile_mode,
52+
resolve_opt_level,
53+
)
4954
from .passes import apply_exir_ops_passes, apply_torch_ops_passes
5055
from .utils import print_ops_info
5156

@@ -364,15 +369,16 @@ def quantize_and_export_to_edge(
364369
def _lower_ep_to_cadence(
365370
program: ExportedProgram,
366371
dump_graphs: bool = False,
367-
opt_level: int = 1,
372+
mode: CompileMode | int | None = None,
368373
edge_passes_config: Optional[EdgePassesConfig] = None,
374+
opt_level: Optional[int] = None,
369375
) -> EdgeProgramManager:
370376
"""
371377
Lower an existing ExportedProgram to edge IR and apply frontend optimization passes.
372378
"""
373379
edge_prog_manager = _lower_ep_to_edge(program, dump_graphs=dump_graphs)
374380
cadence_prog_manager = apply_exir_ops_passes(
375-
opt_level, edge_prog_manager, edge_passes_config
381+
resolve_opt_level(mode, opt_level), edge_prog_manager, edge_passes_config
376382
)
377383
return cadence_prog_manager
378384

@@ -381,12 +387,13 @@ def export_to_cadence(
381387
model: torch.nn.Module,
382388
inputs: tuple[object, ...],
383389
dump_graphs: bool = False,
384-
opt_level: int = 1,
390+
mode: CompileMode | int | None = None,
385391
edge_passes_config: Optional[EdgePassesConfig] = None,
392+
opt_level: Optional[int] = None,
386393
) -> EdgeProgramManager:
387394
edge_prog_manager = export_to_edge(model, inputs, dump_graphs=dump_graphs)
388395
cadence_prog_manager = apply_exir_ops_passes(
389-
opt_level, edge_prog_manager, edge_passes_config
396+
resolve_opt_level(mode, opt_level), edge_prog_manager, edge_passes_config
390397
)
391398
return cadence_prog_manager
392399

@@ -395,8 +402,9 @@ def quantize_and_export_to_cadence(
395402
model: torch.nn.Module,
396403
inputs: tuple[object, ...],
397404
dump_graphs: bool = False,
398-
opt_level: int = 1,
405+
mode: CompileMode | int | None = None,
399406
edge_passes_config: Optional[EdgePassesConfig] = None,
407+
opt_level: Optional[int] = None,
400408
) -> EdgeProgramManager:
401409
"""
402410
Trace, quantize, lower a model/inputs pair to edge IR and apply frontend
@@ -406,6 +414,7 @@ def quantize_and_export_to_cadence(
406414

407415
return _lower_ep_to_cadence(
408416
quantized_model,
417+
mode=mode,
409418
opt_level=opt_level,
410419
dump_graphs=dump_graphs,
411420
edge_passes_config=edge_passes_config,
@@ -416,17 +425,19 @@ def export_to_executorch_gen_etrecord(
416425
model: torch.nn.Module,
417426
inputs: tuple[object, ...],
418427
output_dir: Optional[str] = None,
419-
opt_level: int = 1,
428+
mode: CompileMode | int | None = None,
420429
mem_algo: int = 0,
421430
alloc_graph_input: bool = True,
422431
alloc_graph_output: bool = True,
423432
memory_config: Optional[MemoryConfig] = None,
424433
dump_graphs: bool = False,
434+
opt_level: Optional[int] = None,
425435
) -> ExecutorchProgramManager:
426436
ep = torch.export.export(model, inputs, strict=True)
427437
return _lower_ep_to_cadence_gen_etrecord(
428438
ep,
429439
output_dir=output_dir,
440+
mode=mode,
430441
opt_level=opt_level,
431442
mem_algo=mem_algo,
432443
alloc_graph_input=alloc_graph_input,
@@ -442,15 +453,18 @@ def export_to_executorch_gen_etrecord(
442453
def _lower_ep_to_cadence_gen_etrecord(
443454
ep: ExportedProgram,
444455
output_dir: Optional[str] = None,
445-
opt_level: int = 1,
456+
mode: CompileMode | int | None = None,
446457
mem_algo: int = 0,
447458
alloc_graph_input: bool = True,
448459
alloc_graph_output: bool = True,
449460
memory_config: Optional[MemoryConfig] = None,
450461
dump_graphs: bool = False,
462+
opt_level: Optional[int] = None,
451463
) -> ExecutorchProgramManager:
464+
normalized_mode = normalize_compile_mode(mode, opt_level)
465+
resolved_opt_level = resolve_opt_level(normalized_mode)
452466
edge_prog_manager = _lower_ep_to_edge(ep, dump_graphs)
453-
cadence_prog_manager = apply_exir_ops_passes(opt_level, edge_prog_manager)
467+
cadence_prog_manager = apply_exir_ops_passes(resolved_opt_level, edge_prog_manager)
454468

455469
# Print some information to terminal
456470
print_ops_info(
@@ -463,7 +477,7 @@ def _lower_ep_to_cadence_gen_etrecord(
463477

464478
memory_planning_pass = CadenceMemoryPlanning(
465479
memory_config,
466-
opt_level=opt_level,
480+
mode=normalized_mode,
467481
mem_algo=mem_algo,
468482
alloc_graph_input=alloc_graph_input,
469483
alloc_graph_output=alloc_graph_output,
@@ -483,7 +497,7 @@ def _lower_ep_to_cadence_gen_etrecord(
483497
print_memory_planning_info(
484498
exec_prog,
485499
memory_config,
486-
opt_level,
500+
normalized_mode,
487501
alloc_graph_input,
488502
alloc_graph_output,
489503
)

‎backends/cadence/aot/memory_constraints.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
import torch.fx
1717
from executorch.backends.cadence.aot.pass_utils import (
1818
CadencePassAttribute,
19+
CompileMode,
1920
create_cadence_pass_filter,
21+
normalize_compile_mode,
2022
register_cadence_pass,
23+
resolve_opt_level,
2124
)
2225
from executorch.backends.cadence.aot.utils import get_shape, is_node_in_flattened_output
2326
from executorch.exir import memory
@@ -63,11 +66,16 @@ class MemConstraints:
6366

6467
def __init__(
6568
self,
66-
opt_level: int = 1,
69+
mode: CompileMode | int | None = None,
6770
alloc_graph_input: bool = True,
6871
alloc_graph_output: bool = True,
72+
opt_level: Optional[int] = None,
6973
) -> None:
70-
self.opt_level = opt_level
74+
normalized_mode = normalize_compile_mode(mode, opt_level)
75+
self.opt_level = resolve_opt_level(normalized_mode)
76+
self.mode: Optional[CompileMode] = (
77+
normalized_mode if isinstance(normalized_mode, CompileMode) else None
78+
)
7179
self.alloc_graph_input = alloc_graph_input
7280
self.alloc_graph_output = alloc_graph_output
7381

‎backends/cadence/aot/memory_planning.py‎

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
MemoryPlanningAlgo,
2020
MemoryPlanningState,
2121
)
22+
from executorch.backends.cadence.aot.pass_utils import (
23+
CompileMode,
24+
normalize_compile_mode,
25+
resolve_opt_level,
26+
)
2227
from executorch.backends.cadence.aot.utils import (
2328
MemoryConfig,
2429
MemoryPlanningAlgoFailure,
@@ -287,13 +292,15 @@ def find_peak_memory_usage(
287292
def print_memory_planning_info(
288293
executorch_prog: ExecutorchProgramManager,
289294
memory_config: MemoryConfig,
290-
opt_level: int,
291-
alloc_graph_input: bool,
292-
alloc_graph_output: bool,
295+
mode: CompileMode | int | None = None,
296+
alloc_graph_input: bool = True,
297+
alloc_graph_output: bool = True,
293298
log_level=logging.INFO,
299+
opt_level: Optional[int] = None,
294300
) -> None:
295301
# Get the peak memory usages per memory space
296302
mem_constraints = MemConstraints(
303+
mode=mode,
297304
opt_level=opt_level,
298305
alloc_graph_input=alloc_graph_input,
299306
alloc_graph_output=alloc_graph_output,
@@ -397,20 +404,25 @@ class CadenceMemoryPlanning:
397404
def __init__(
398405
self,
399406
memory_config: MemoryConfig,
400-
opt_level: int,
401-
mem_algo: int,
407+
mode: CompileMode | int | None = None,
408+
mem_algo: int = 0,
402409
alloc_graph_input: bool = True,
403410
alloc_graph_output: bool = True,
404411
additional_constraint_gen_passes: Optional[Sequence[ConstraintsGenPass]] = None,
412+
opt_level: Optional[int] = None,
405413
) -> None:
406414
self.memory_config = memory_config
407-
self.opt_level = opt_level
415+
normalized_mode = normalize_compile_mode(mode, opt_level)
416+
self.opt_level = resolve_opt_level(normalized_mode)
417+
self.mode: Optional[CompileMode] = (
418+
normalized_mode if isinstance(normalized_mode, CompileMode) else None
419+
)
408420
self.alloc_graph_input = alloc_graph_input
409421
self.alloc_graph_output = alloc_graph_output
410422

411423
self.algo: MemoryPlanningAlgo = self.get_mem_algos(
412424
memory_config,
413-
opt_level,
425+
normalized_mode,
414426
alloc_graph_input,
415427
alloc_graph_output,
416428
additional_constraint_gen_passes,
@@ -419,22 +431,24 @@ def __init__(
419431
@staticmethod
420432
def get_mem_algos(
421433
memory_config: MemoryConfig,
422-
opt_level: int,
423-
alloc_graph_input: bool,
424-
alloc_graph_output: bool,
425-
additional_constraint_gen_passes: Optional[Sequence[ConstraintsGenPass]],
434+
mode: CompileMode | int | None = None,
435+
alloc_graph_input: bool = True,
436+
alloc_graph_output: bool = True,
437+
additional_constraint_gen_passes: Optional[Sequence[ConstraintsGenPass]] = None,
438+
opt_level: Optional[int] = None,
426439
) -> list[MemoryPlanningAlgo]:
440+
normalized_mode = normalize_compile_mode(mode, opt_level)
427441
return [
428442
PositionBasedGreedyWithHierarchy(
429443
memory_config=memory_config,
430-
opt_level=opt_level,
444+
mode=normalized_mode,
431445
alloc_graph_input=alloc_graph_input,
432446
alloc_graph_output=alloc_graph_output,
433447
additional_constraint_gen_passes=additional_constraint_gen_passes,
434448
),
435449
GreedyWithHeuristic(
436450
memory_config=memory_config,
437-
opt_level=opt_level,
451+
mode=normalized_mode,
438452
alloc_graph_input=alloc_graph_input,
439453
alloc_graph_output=alloc_graph_output,
440454
additional_constraint_gen_passes=additional_constraint_gen_passes,

‎backends/cadence/aot/memory_planning_algo.py‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
GenerateMemConstraints,
1616
MemConstraints,
1717
)
18+
from executorch.backends.cadence.aot.pass_utils import (
19+
CompileMode,
20+
normalize_compile_mode,
21+
resolve_opt_level,
22+
)
1823
from executorch.backends.cadence.aot.utils import MemoryConfig
1924
from executorch.exir.memory_planning import Verifier
2025
from executorch.exir.tensor import TensorSpec
@@ -85,16 +90,21 @@ class MemoryPlanningAlgo(ABC):
8590
def __init__(
8691
self,
8792
memory_config: MemoryConfig,
88-
opt_level: int = 1,
93+
mode: CompileMode | int | None = None,
8994
alloc_graph_input: bool = True,
9095
alloc_graph_output: bool = True,
9196
additional_constraint_gen_passes: Optional[Sequence[ConstraintsGenPass]] = None,
97+
opt_level: Optional[int] = None,
9298
) -> None:
9399
self.memory_config: MemoryConfig = memory_config
94100
self.additional_constraint_gen_passes: Optional[
95101
Sequence[ConstraintsGenPass]
96102
] = additional_constraint_gen_passes
97-
self.opt_level: int = opt_level
103+
normalized_mode = normalize_compile_mode(mode, opt_level)
104+
self.opt_level: int = resolve_opt_level(normalized_mode)
105+
self.mode: Optional[CompileMode] = (
106+
normalized_mode if isinstance(normalized_mode, CompileMode) else None
107+
)
98108
self.alloc_graph_input: bool = alloc_graph_input
99109
self.alloc_graph_output: bool = alloc_graph_output
100110
self.memory_id_is_valid: list[bool] = [True] * self.get_num_memories()

‎backends/cadence/aot/pass_utils.py‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
# pyre-strict
88

99
import dataclasses
10+
import warnings
1011
from dataclasses import dataclass
12+
from enum import Enum
1113
from typing import Callable, List, Optional, Set, Type, Union
1214

1315
import torch
@@ -25,6 +27,59 @@
2527
from torch._ops import OpOverloadPacket
2628

2729

30+
class CompileMode(Enum):
31+
"""Selects which pass pipeline the Cadence backend runs.
32+
33+
During migration, omitting ``mode`` preserves the legacy ``opt_level=1``
34+
behavior, while explicitly passing ``DEFAULT`` selects the former O3
35+
pipeline with all graph optimizations.
36+
"""
37+
38+
MINIMAL = "minimal"
39+
DEFAULT = "default"
40+
SIZE = "size"
41+
42+
43+
def opt_level_from_compile_mode(mode: CompileMode) -> int:
44+
"""Translate the new API to the legacy pass levels during migration."""
45+
if mode is CompileMode.MINIMAL:
46+
return 0
47+
if mode is CompileMode.SIZE:
48+
return 4
49+
return 3
50+
51+
52+
def resolve_opt_level(
53+
mode: CompileMode | int | None = None,
54+
opt_level: Optional[int] = None,
55+
) -> int:
56+
if opt_level is not None:
57+
if mode is not None:
58+
warnings.warn(
59+
"Both mode and opt_level were provided; deprecated opt_level takes "
60+
"precedence.",
61+
DeprecationWarning,
62+
stacklevel=2,
63+
)
64+
return opt_level
65+
if mode is None:
66+
return 1
67+
if isinstance(mode, int):
68+
return mode
69+
return opt_level_from_compile_mode(mode)
70+
71+
72+
def normalize_compile_mode(
73+
mode: CompileMode | int | None = None,
74+
opt_level: Optional[int] = None,
75+
) -> CompileMode | int:
76+
"""Resolve compatibility arguments while preserving an explicit enum mode."""
77+
resolved_opt_level = resolve_opt_level(mode, opt_level)
78+
if opt_level is None and isinstance(mode, CompileMode):
79+
return mode
80+
return resolved_opt_level
81+
82+
2883
# Is an overlap in tensor lifetime and storage allowed at the current opt level?
2984
# We allow overlap at opt level >= 2.
3085
def allow_lifetime_and_storage_overlap(opt_level: int) -> bool:

0 commit comments

Comments
 (0)